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 001/217] [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 da4bb40a2e7..4a13f5318f7 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 0103c6b76a2..319c95ad611 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: -- GitLab From 2993b0008e2ec4040be91868bf5f48a892508c3a Mon Sep 17 00:00:00 2001 From: Egor_P Date: Fri, 9 Aug 2024 10:01:55 +0200 Subject: [PATCH 002/217] 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 33ef2d3e7ed..bfb3120ad9b 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 cda10f2ebf1..f09ecf1c799 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: | -- GitLab From 87280eb52537a355c67c9b9cee8ff1f97d02d67a Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 9 Aug 2024 13:03:07 +0200 Subject: [PATCH 003/217] 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 d22dc8724f3..c06beb5e98e 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 }}" -- GitLab From b49509baf07a8938318e6e0b46cd266f935a936b Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Fri, 9 Aug 2024 15:45:35 +0300 Subject: [PATCH 004/217] 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 c8face15627..c65880771e0 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 7384bf2850f..1c26742fd67 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() + ) + } + }); } } -- GitLab From 32b87605182aabdc3e95aa1882ef7bca1ce34e46 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 9 Aug 2024 15:53:27 +0300 Subject: [PATCH 005/217] [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 82f86e2b32e..5b70ed490c6 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 fd84030ed13..d20e059f9fe 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 260875088bb..38c94b34aa2 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 8b9fedcd494..313fa953dd0 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 f8190e11c51..3320392b495 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 15f4fe33bdd..9520659712f 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 65d013a0eec..82ef74fdab1 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 8cbce3e0d22..b7096718438 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 eca7c7bbc3c..d0c421bccaf 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 -- GitLab 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 006/217] [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 00000000000..054c7d786ca --- /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 - -- GitLab From 862860ecc89e077331b72eb66cd7305b931fb9e8 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Sat, 10 Aug 2024 00:06:59 +0900 Subject: [PATCH 007/217] 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 f48dd927ee9..5984fa77a2c 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 00000000000..981172c6c13 --- /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 -- GitLab From 380cd21877843d7668139725c98762c6f617fada Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Fri, 9 Aug 2024 18:25:22 +0300 Subject: [PATCH 008/217] 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 60698de1d6a..55b9156e6a0 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 -- GitLab 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 009/217] 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 54a01f12f35..4db38311fe6 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 13ab3e3fba5..fcacc38cae6 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 1985964ebc5..103d29e8d26 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 28fc957ddb1..1459907aa59 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 86d855f78b4..55282fdf4ee 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 97a03e6596d..342128b7cca 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 7ee05448d3c..b0cdba9501d 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 46862f9f80b..cff3f3b86e9 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 81e165a7b8a..4cd1beb3099 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 e2ac36a2406..4019a8d8b0d 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 f24b66dc4a0..6ad340d2561 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 35858ab36ce..4b7c167cc9e 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 9e0d01fc438..56235bd8219 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 d531c90b64b..49b30dc33ce 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 ef33d11720e..f8ebb6effce 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 8dc96305ead..a0634106052 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 bb00a5a652d..11031bf1074 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 d15d7c15426..77bd6bedd75 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 462631d33b5..44a4cba2fbf 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 bc8d300037f..c59cab46418 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 22ee93319d8..d29d2717c4b 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 455e8c36c88..1a95a28fe07 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 9ad48665751..a4a08531895 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 b8fd2cdce0c..b3023c8a45c 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 4e13d5eda76..baaff9c7c9f 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 00000000000..4083e5bf53c --- /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 -- GitLab From 149c70938f2b29f8d92ba1cc952aeb63d4084e27 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 9 Aug 2024 18:11:50 +0200 Subject: [PATCH 010/217] 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 b556a453608..180c229e744 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 70b24c19f8e..da5073ea687 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 802d8586b39..b7fa2bed0ed 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 7cf1f1fddc7..c782888a3e8 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 c5dc5db7f3b..6a5a7853f9c 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 6eebf3694e3..9dd1b144d22 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", -- GitLab From 0b52a2c19ebcf5d7a0d07974b70aec656704d249 Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Mon, 12 Aug 2024 11:03:23 +0300 Subject: [PATCH 011/217] 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 4db38311fe6..6ebacc9ec5a 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 97da5a1e94a..705014e67a0 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 2b0933ab1c7..4b332b9c5de 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 b5fe70e7692..b060897d439 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 26ee94d59d8..9886d19e522 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 e4b6deffdf4..ecb1f3a476e 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 5abd9f56f30..78561bc878a 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 221fbf4c4e6..14a093239e8 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 3f622a60a05..ffc5859b775 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 47d350849b2..109c29f520c 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 4d27ac9b70e..d067ca46801 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 23a758d2571..a805ef8165e 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 2272f048089..0c3b4074349 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 00000000000..37b7bc3dda5 --- /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 -- GitLab 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 012/217] 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 7077fbbb0a9..b3dec175e11 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 6309c058410..1a30fac6ba9 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 313fa953dd0..329dbcac4b4 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 060c3fb3925..437268a19e5 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 82ef74fdab1..729de65382f 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 40a1968ec55..3e8d3357caa 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 3c0533f775e..b725c24fbea 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 4410d1bd40d..c28b305b2c9 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 689409fe504..386a1b91c85 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 6fd3cdeb61f..29458339901 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 03d3785dccb..3d6ae6ddd1d 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 fe8d1861392..f7891aedc49 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 057dc431351..f2cee0e3e80 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 4310b245647..5bd1d1680aa 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 73a71980530..c8dbdadf7b1 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 ddfc599fa57..935636651eb 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 11c1bad9aa1..58007173ae1 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 2364798596d..4dd44e66dd5 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 a50c8860c48..729a3211704 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 b1fc7ad8ed8..b44e8d4b61b 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 92d08a24618..8f6bfde986b 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 861f0381995..1377d31f2db 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 60c40429b1a..7d743b20912 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 9939f16aa29..e0c61c8e2bf 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 1daf5ae750c..5101a97dc84 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 00000000000..81636c3313f --- /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 -- GitLab 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 013/217] 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 bc0970c01f1..6dc87858530 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" )] -- GitLab From fc906d5d0fb7796ef54ba670101cf37b0aad6794 Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Mon, 12 Aug 2024 16:56:00 +0300 Subject: [PATCH 014/217] 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 ec2c01f99b0..d3185e0af80 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 efbdceb43bd..0175161af70 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 09567a8f87d..decb3156004 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 00000000000..97f75d28dd5 --- /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 -- GitLab 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 015/217] 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 c43f9e89b8a..5451428d348 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. -- GitLab From bcc96733f7940edea9f3a782aea78a92c8bb61cf Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 12 Aug 2024 22:01:02 +0300 Subject: [PATCH 016/217] 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 35fc8ad4a40..1baa67398a4 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 05f2b252796..6a307acb251 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 690a579e027..dad5a27e953 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!(), -- GitLab From aca25a009f7492d3c5ef07d62363f6812688355b Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Mon, 12 Aug 2024 20:49:44 +0100 Subject: [PATCH 017/217] 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 2825f4a6046..afdcf4c1b7b 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 -- GitLab 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 018/217] `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 18b3f959c95..bf663b4cfce 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] -- GitLab 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 019/217] 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 6ebacc9ec5a..87ff24663ff 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 7ae7c3bd181..2333dd15fce 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" } -- GitLab 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 020/217] 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 d8f956b82d2..2e2d7a7fb4f 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 cc1082347d2..5ac99fff1cd 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 083b30b4a35..9612ab6d8d3 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 0d2064863d8..f52da032713 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: -- GitLab From 819a5818284a96a5a5bd65ce67e69bab860d4534 Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 12 Aug 2024 22:34:22 +0200 Subject: [PATCH 021/217] 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 80698a2d711..3b9ae6bd445 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 a9fda2c3aa8..25ef3a12cbf 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 00000000000..cfd435fa289 --- /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 -- GitLab 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 022/217] 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 87ff24663ff..33ebf14be0e 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 2333dd15fce..74b12f96603 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" } -- GitLab 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 023/217] 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 00000000000..7c19b420a6a --- /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' }} + -- GitLab From 0d7bd6badce2d53bb332fc48d4a32e828267cf7e Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 13 Aug 2024 13:11:12 +0200 Subject: [PATCH 024/217] 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 33ebf14be0e..60be4156329 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 062b9ce736e..185b2d40833 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 172af4b9ec6..a4271d3fd9c 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 82d18c8c0aa..2ca8b82001d 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 5101a97dc84..74561e931e7 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. // -- GitLab From b78d7955e280309dc7cbd6b0b40819e942dfc597 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 13 Aug 2024 15:25:09 +0200 Subject: [PATCH 025/217] [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 793524e056f..ce4e0fd0d78 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 00000000000..b7b2e6f970f --- /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 00000000000..da8f14089cb --- /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' -- GitLab From 055eb5377da43eaced23647ed4348a816bfeb8f4 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Tue, 13 Aug 2024 21:57:23 +0200 Subject: [PATCH 026/217] 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 5984fa77a2c..a557e881e26 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 00000000000..60feab412af --- /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 0c6ff2cb8dd..abacfa7b62c 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] -- GitLab 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 027/217] [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 00000000000..41e3886405c --- /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 f8df9dfe7b4..8c05b0bcfc2 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 8203f751330..1882989cfa7 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 ade0872dd39..bc8bc2dccc3 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 24b45735654..aff282774c3 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 4d571855e4f..272b3b60612 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 70ad06e2a4d..9108060ccb2 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 28063c2ecae..06261699a5b 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 51f6470f90d..a50a6b73f5f 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 ed47932a323..d1bc4ef8ff2 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 aa913502590..28e978bba0e 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 03c1af50240..5e94524816a 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", $); -- GitLab From 0cd577ba1c4995500eb3ed10330d93402177a53b Mon Sep 17 00:00:00 2001 From: Jeeyong Um Date: Wed, 14 Aug 2024 06:54:51 +0900 Subject: [PATCH 028/217] 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 00000000000..a3244a82c86 --- /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 d313d23395a..fd10dee2a7c 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) }, } } -- GitLab From be74fe921add60f79e2d11548bfa5a7d17e063ff Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 14 Aug 2024 10:06:16 +0200 Subject: [PATCH 029/217] 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 60be4156329..2593452fb86 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 80d2376c681..1f98d3ba964 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 608690218d2..6066adec52c 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 b3dec175e11..30e66ced1fb 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 364fbd0d439..ab407611c73 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 16e0512da96..ac0c90ba198 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 f74378d7631..5f9131b8c12 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 437268a19e5..fe484771931 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 eb0e985cc0c..ac006653ca6 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 cf429378cf6..1a282145215 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 a524b87b2da..02a0bc0207b 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 3ee509389c6..0aefe5b6352 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 6053936487b..8a674f89c9e 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 ceccf98a024..6ce8ecef0df 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 652447fa560..3f2038b4bdd 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 782b83bac47..3262cc17ba5 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 0c0b04cd45a..e2c496802e2 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 87ae9aedd6f..bf894a3baf5 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 7414adacc44..4c7356707ab 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 00000000000..12c0bc4e168 --- /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 a11dca4f6d7..2d1914e059b 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 ee1461b7f9c..83f4f9ec3dc 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 201698ecb7f..ebbc000d141 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 bf45e146e33..d61381d3f50 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 48e6c11d268..1c334d6f84f 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 884b71369e7..67b585ecfe8 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 0b2364dbb8b..e0b3f70c754 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 00000000000..dfcc9b9ef03 --- /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 fcb151298f0..e20b576d083 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 } -- GitLab From d944ac2f25d267b443631f891aa68a834dc24af0 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Wed, 14 Aug 2024 10:46:03 +0200 Subject: [PATCH 030/217] 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 68625e5433c..32616871767 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 -- GitLab From e4f8a6de49b37af3c58d9414ba81f7002c911551 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Wed, 14 Aug 2024 13:58:07 +0300 Subject: [PATCH 031/217] [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 2593452fb86..67cfbb5968d 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 43d182facdd..36a701d24c2 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 0b99f19bc13..942ec1b31d2 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 b11adacbde5..578bca84ce5 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 76179c6d82c..7a289a3f1ac 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 1a30fac6ba9..87a090bf1ae 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 329dbcac4b4..70dde03d75a 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 ab407611c73..29eaa969464 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 5f9131b8c12..c8da801a14b 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 fe484771931..a887ee6a532 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 729de65382f..59f63d38059 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 ac006653ca6..761c7c12255 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 02a0bc0207b..15d39858acc 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 0aefe5b6352..ac08e48ded6 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 3e8d3357caa..84328fb7c6d 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 1fb03748d92..8cdd9613dc5 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 3262cc17ba5..5e0462d1488 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 e2c496802e2..fc8b772a9c7 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 64378a844f5..a5add3b8295 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 3012e2b19f5..c4d281b75a7 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 00000000000..f97599bda7f --- /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 00000000000..840d2da4946 --- /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 40e98a8b686..ef4e4885183 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 b725c24fbea..06b0b6ba600 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 3f18621224a..08749b295dc 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 10f0c61ed63..00000000000 --- 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 c28b305b2c9..44e6b3934f0 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 f7e1cce85a2..aa6eebc5458 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 386a1b91c85..418cfea07dd 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 3f18621224a..08749b295dc 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 cfbf4d7d958..00000000000 --- 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 29458339901..83888031723 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 ae4fe9e8433..08b1d192b0b 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: -- GitLab From 00946b10ab18331f959f5cbced7c433b6132b1cb Mon Sep 17 00:00:00 2001 From: Muharem Date: Wed, 14 Aug 2024 15:35:34 +0200 Subject: [PATCH 032/217] 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 00000000000..bf059129a43 --- /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 1a09303a659..e1c1be9b133 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 2d3bec16b81..3d0c5b90057 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 30056fc6d9a..658e7fec534 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 a954af14d25..eb63ea59f6c 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 f40e494b930..9b7c86971bb 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))); + } } -- GitLab 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 033/217] 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 56965ce6ba4..7745c42f78a 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 00000000000..a306be33544 --- /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 -- GitLab From 81d8f0c0fa0be50dcd2c0291840a1b5b7ace8b98 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 14 Aug 2024 21:28:18 +0300 Subject: [PATCH 034/217] 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 67cfbb5968d..9e3afe21c44 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 2f23b889916..30b915d32de 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 0512a393a6c..cd3f4689f56 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 00000000000..317c9149ec6 --- /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 838ba17e561..aa446b03368 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 2248e421e63..cb6e2c85e36 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 00000000000..5be207e3fcf --- /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 00000000000..b2ab9ff6653 --- /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 a94838cf20c..ff3f7e26baf 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 672a7a68bc7..d67ac20ee92 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 00000000000..135f95eabb9 --- /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 195bbfbf2f2..73119c3faa9 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 1102f9677aa..6756c618d70 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 f99835a1dc0..b126a01012b 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 00000000000..c292f25400c --- /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 70dd3bb02bf..6b83015459d 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 353ba876c7e..cf690a9df33 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 b423fa0bda8..5c79d8f7d7d 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 a63b3532b69..d75237205ca 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 0ab44711bcf..7dfe95c8336 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 2b46357c507..f9a4580b9bb 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 a3908980148..02852388b41 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 f31aa5756ba..57ddab9a70c 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 6ec4a727e27..e977fb0ea25 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 72674e24a27..2460af4b800 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) } -- GitLab From d8c2944741b1f1b43fa37951fb311d363c753e4e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 14 Aug 2024 21:22:08 +0200 Subject: [PATCH 035/217] [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 2b963b2230f..9b7a6fafcd1 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 da8f14089cb..3a08b9a5fb2 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 -- GitLab From 5a9396f41e3cb7d94da3a9305512783229bc278b Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Wed, 14 Aug 2024 22:44:59 +0300 Subject: [PATCH 036/217] 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 00000000000..4b07f30bfd0 --- /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 d7fb7918481..90bfd9ec27f 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, }, -- GitLab 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 037/217] [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 aa446b03368..a9fbbb4f33d 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 4d5e2e946bc..dc8103ab52c 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 00000000000..44f3e41d8d5 --- /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 ff3f7e26baf..ea9a66862d6 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 8c05b0bcfc2..4e6812dee24 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 1882989cfa7..08ce747ec0c 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 bc8bc2dccc3..2c965e18b1b 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 aff282774c3..a78aa3f5590 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 67627e0acb1..d81ad1dd495 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 9108060ccb2..44e3463dc9f 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 a50a6b73f5f..7fee2a0bdb2 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 },] -- GitLab 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 038/217] Upgrade accidentally downgraded deps (#5365) --- Cargo.lock | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e3afe21c44..cc8b64457a7 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" -- GitLab 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 039/217] [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 cc8b64457a7..76973e3397b 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 a9fbbb4f33d..5f8d5d42493 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 00000000000..d396fcdbe8b --- /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 1bd17c59f1e..8d5ccd342b6 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 08ce747ec0c..7b8d14b0a61 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 00000000000..8bc7312c4ea --- /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(()) + } + } + } +} -- GitLab 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 040/217] [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 fbd8935f199..9b9bdb86878 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 00000000000..1baa5e1cbe7 --- /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 -- GitLab 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 041/217] 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 76973e3397b..2bad3947edf 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 74b12f96603..edb425e619c 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" } -- GitLab 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 042/217] 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 0203656eff4..94b2ae0231d 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 -- GitLab 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 043/217] 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 4efd50a04ec..0f9583cd0eb 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 00000000000..4b055d81cb5 --- /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 -- GitLab From a6dffe390c24011fcf3fabab83c46d82f9410400 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 15 Aug 2024 13:48:17 +0200 Subject: [PATCH 044/217] 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 a95b24bc293..3127dd26fca 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 4c9e61458a8..dc854eb8201 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 f3e03aa7430..f85861bdaa5 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 00000000000..828f0ffeb1b --- /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 2bd791f5238..7f7d1f2b50e 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 e910fc32371..a347174ed2e 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 07f1cbad126..7bdfcd0b03a 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; -- GitLab 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 045/217] 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 1687a03520a..60d7c639c88 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, } -- GitLab From b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 15 Aug 2024 16:55:49 +0200 Subject: [PATCH 046/217] 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 2bad3947edf..df01a143efb 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 2f85171bb93..424758c32b3 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 3c74469e176..006d0be7ded 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 557795cb410..39e5993d020 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 4a0ba3ca48f..c91b66b944c 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 -- GitLab 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 047/217] 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 00000000000..869f17661e4 --- /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 00000000000..ebcf6c5fc9b --- /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 -- GitLab 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 048/217] 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 00000000000..c2282c4c74c --- /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 7c8c22d1ae5..492475d6f63 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 { -- GitLab From 843c4db78f0ca260376c5a3abf78c95782ebdc64 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 15 Aug 2024 19:40:08 +0200 Subject: [PATCH 049/217] 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 47af79a3aa9..fa14995e9af 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 00000000000..0301b8c17a3 --- /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 -- GitLab From 80c3c1fd712ce9f1dd0c539e384dbeab699485ed Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 16 Aug 2024 08:33:31 +0200 Subject: [PATCH 050/217] 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 ef4a5597902..54633449134 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 27287118547..7b343ed97a8 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) { -- GitLab From dbd194aad4b20ff03ed74e64cf06cb59d4396788 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Fri, 16 Aug 2024 10:05:40 +0300 Subject: [PATCH 051/217] 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 5f86da87f21..5096fe5e689 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 daa384b36ff..34d9ddf3a97 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 00000000000..fd4454ac3b9 --- /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 -- GitLab 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 052/217] 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 3462aaef1f6..134586253ce 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 3ea722c51a9..1ca571721ea 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, )); -- GitLab From 74267881e765a01b1c7b3114c21b80dbe7686940 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 16 Aug 2024 12:29:04 +0200 Subject: [PATCH 053/217] 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 df01a143efb..4148b660cd3 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 edb425e619c..ebe9dc0dab3 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 ba96e139bcf..00000000000 --- 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 68825d190bb..00000000000 --- 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 -//! ``` -- GitLab From fd522b818693965b6ec9e4cfdb4182c114d6105c Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Fri, 16 Aug 2024 23:09:06 +0900 Subject: [PATCH 054/217] 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 00000000000..38a73b6b00e --- /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 6f8ad0c2939..e909932bfc8 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 9fa1df86209..52d3e8c014b 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. -- GitLab From feac7a521092c599d47df3e49084e6bff732c7db Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sun, 18 Aug 2024 08:23:46 +0300 Subject: [PATCH 055/217] 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 e12750dcc55..6766c2409c3 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 944917673b1..861354ed63c 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 284fa39ed1e..06f90330d47 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 18d121c41d1..cde73c4c518 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 38ba84748c1..c796dc5f7c3 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 1527492f578..a44d1750781 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 6ec338c7f14..6fe26519a3e 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 4ace894b392..67ffbdd1d21 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 6b205c09f2f..498994d9a0a 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 279d7118f8c..7683c602539 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 00000000000..850ba903e12 --- /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 8239637b3a9..9ac9d8b4f67 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 0a2e3fd2504..037ddbb1e47 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 74805488792..527a3d12d9e 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 0c1eb887586..9770b16871e 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 6f805188b9a..5c2e0eae959 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 84802685293..17a4a586663 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 4b1dd447320..afa6191d8bf 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 c5adbb5a5fc..4d7b89f37d8 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 6a307acb251..7b371145e2e 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 af965f2e4ae..8a47d121e86 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 8b7b02f180e..5cec5204c73 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 2aa1b5f6ee1..9cca28a3959 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 c37596d20f6..c1d3cd2fbd6 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 c836ab09fd5..a79581b1e9f 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 50e9533abb3..cd7da128549 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 9e9c4fc137d..73400136483 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 fcf9fa25b59..3b0506ef55d 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 22f1973adcb..1e20572eeeb 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 72f84d1c286..58befe94e84 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 cd955113542..39d0c8f8d4d 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 dad5a27e953..8c120d9de66 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 6ac20842a33..9285306948a 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 48d3b8f1393..7cee64e6ce7 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 de71ed82a12..078016f5b3e 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 d4d616f54dc..14325b4fbb9 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 b195e05b664..38f091471f8 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 afb81f709f7..9d78aa88091 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 e8f9ba4990d..8f09d48e8a5 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 dd866e671c5..86df46f63a0 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 a2c9212f7b9..22defd7c551 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 bd48fae6344..13e63962fe8 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 49bd2203c12..6d70b6ce67e 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 9dc4739eb79..a4f91f2ec83 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 6f6bb5c36ee..c3a5f173d14 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 d1a3eaa2daa..514f3bcba20 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 -- GitLab 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 056/217] 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 00000000000..c9874ef7d86 --- /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 030e9f69b9b..f2d338cf028 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); } -- GitLab From a4b51593dbe9b9390fa64f5040a9230b5d33b898 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Mon, 19 Aug 2024 15:05:54 +0200 Subject: [PATCH 057/217] 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 f55c31b5321..c24c1094075 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; -- GitLab From 946afaabd8244f1256f3aecff75e23c02937bd38 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Mon, 19 Aug 2024 17:47:50 +0200 Subject: [PATCH 058/217] 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 f09ecf1c799..a749c86faa0 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 -- GitLab From 37c20272c9fb93a37e828f539da20729d0f22820 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Tue, 20 Aug 2024 14:01:13 +0300 Subject: [PATCH 059/217] 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 4e750886755..d4be135aad6 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 30b915d32de..31713755b9b 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 5f8d5d42493..519c7dcde54 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 00000000000..75063e33534 --- /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 -- GitLab 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 060/217] 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 134586253ce..a1bdc47e9fb 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 00000000000..c24001d77bd --- /dev/null +++ b/prdoc/pr_5411.prdoc @@ -0,0 +1,3 @@ +crates: + - name: polkadot-approval-distribution + bump: none -- GitLab From 73e2316adad1582a2113301e4f2938d68ca10974 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Tue, 20 Aug 2024 16:02:19 +0300 Subject: [PATCH 061/217] 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 6583173e40c..20d0c2a988b 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) => { -- GitLab From 717bbb24c40717e70d2e3b648bcd372559c71bd2 Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Tue, 20 Aug 2024 16:16:57 +0100 Subject: [PATCH 062/217] 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 00000000000..523c2b19ba8 --- /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 -- GitLab From 5ca3d2ebdfdaffce7a6678a26bbda8e9672b6bf6 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 21 Aug 2024 18:28:12 +0300 Subject: [PATCH 063/217] 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 4148b660cd3..a67f9fdee31 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 ebe9dc0dab3..397163b3cce 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 5d654844746..383e0f158bf 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 00000000000..09bde034cf2 --- /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 00000000000..2aa2b10fbb6 --- /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 00000000000..7f915b729e0 --- /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 9f72d847926..9e8837de7f8 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 00000000000..974d6ef2b61 --- /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 d7718931b87..89bc7511dac 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 00000000000..c64eda12d5e --- /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 7d54e9b4be0..f2b8b4d562b 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 ca5fc8bdf11..eb6d3fafa3d 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 00000000000..fc164a9d890 --- /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 af5bccdc416..f6bf6375a35 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 3b7376d045b..754bd851b40 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 c0a9f195d89..865a2a91708 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 4e89b81d1be..eb10a43ffbe 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 fe60b09fd8b..742f41f39e9 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 77a4123b13e..136411b93e8 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 19047b073b0..de9c6a889ed 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 cb1cb632d63..5645bf06b67 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 6100a15018c..3c1150d9542 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 0434e5f7be8..9f4a162e67f 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 32d51622054..a104b58db5d 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 e0a9875fb96..0a7816ab319 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 a5fe33dffc9..00000000000 --- 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 3df90c889e8..00000000000 --- 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 84e41fc347d..f2dce552c51 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 939043b5352..38ef18b88e0 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 00000000000..8241e75876f --- /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 65ff9a81e47..2bfed05cd17 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 7bdfcd0b03a..5820fa80354 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; -- GitLab From 646e4f7ed3eae1de98d34da2a5c785180c2ea1a9 Mon Sep 17 00:00:00 2001 From: Diego Date: Wed, 21 Aug 2024 12:41:57 -0300 Subject: [PATCH 064/217] 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 a7956cfcd42..cb9a9267f77 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 -- GitLab 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 065/217] 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 00000000000..f7e6b86f9d1 --- /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 55e912c31f1..dd8d92bcee6 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 63e98a2fb4b..0e57791542e 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, -- GitLab 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 066/217] 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 00000000000..73e104b3829 --- /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 00000000000..f6f2bc68691 --- /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 742f41f39e9..fec3f56e6d3 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 00000000000..ea624b7bc32 --- /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 -- GitLab From e600b74ce586c462c36074856f62008e1c145318 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Thu, 22 Aug 2024 13:57:37 +0200 Subject: [PATCH 067/217] [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 24c69582731..685a9fd337d 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 -- GitLab From b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1 Mon Sep 17 00:00:00 2001 From: eskimor Date: Thu, 22 Aug 2024 16:15:18 +0200 Subject: [PATCH 068/217] 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 2409e6994f6..77c1e41aac0 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 00000000000..aeeb05de0bc --- /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 -- GitLab From 6d819a61f0bd337383b9e0f355753802737e710c Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 23 Aug 2024 10:38:16 +0300 Subject: [PATCH 069/217] 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 9b5f0bec538..7f656aabca7 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 5ca566d2c96..e1f42e1ca86 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 00000000000..9f6db7136a5 --- /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 d58ca888d41..d45713db522 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 655bf21c711..f5e042103ea 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 ee7576c22f1..a25db4f789d 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 9f6c0f45d08..ca7280edba5 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 58befe94e84..2c9799a9d83 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 00855578695..4cc3e9f3a68 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 9285306948a..b5aeb162e9f 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 f1c1b741430..9edd68c2ed9 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 90bfd9ec27f..d9a91e715fd 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 89d563001cd..ed108a3102b 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 44b374fcc0a..decb9d6c636 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 5e84552f4cc..7eef9766b10 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, })?; -- GitLab From 4ffccac4fe4d2294dfa63b20155f9c6052c69574 Mon Sep 17 00:00:00 2001 From: Gustavo Gonzalez Date: Fri, 23 Aug 2024 05:17:28 -0300 Subject: [PATCH 070/217] 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 e87eb9c2bc8..03a7aa98198 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 //! -- GitLab 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 071/217] 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 24441e90b1a..7a67ba2695c 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 ee4bd62a558..9bdf64c9979 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 a67f9fdee31..89cc7cfa8d4 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 397163b3cce..275efe1df63 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 00000000000..90528a224e8 --- /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 3293c30bc82..5b9cc89c530 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 5b827c9d718..6e734a723cd 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 2ad65588391..c262d74fa8a 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 ea9a66862d6..ef5c52bf6e6 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 00000000000..747c2283e21 --- /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 00000000000..5352e636c25 --- /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 00000000000..ca8e62df604 --- /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 00000000000..1b668c061f8 --- /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 00000000000..ed981a75467 --- /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 00000000000..7dead51b230 --- /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 00000000000..4011b8379cb --- /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 00000000000..a75aee65c20 --- /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 00000000000..32654d59ef1 --- /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 00000000000..2b132398fb6 --- /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 00000000000..1323c8c5d55 --- /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 00000000000..a078162d799 --- /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 00000000000..a5356924f24 --- /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 00000000000..2fa11df82d0 --- /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 00000000000..fd6f59802fa --- /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 00000000000..474df00d691 --- /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 00000000000..c7596e44dab --- /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 00000000000..7dadb7b821a --- /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 00000000000..6631af8292f --- /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 00000000000..d0d3651dfe4 --- /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 00000000000..918a4abe6b2 --- /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 00000000000..a2a0e85bcf6 --- /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 00000000000..35cc03f1e72 --- /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 00000000000..6c850a9ec66 --- /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 00000000000..0ce2b6b5628 --- /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 00000000000..3a2509509d8 --- /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 00000000000..d03ddab1bc5 --- /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 00000000000..055760729bd --- /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 00000000000..cf3351c52fd --- /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 00000000000..8b74493a1e3 --- /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 00000000000..f5c8681c938 --- /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 00000000000..c7294e99139 --- /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 00000000000..0f28ca2c819 --- /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 00000000000..9186835d291 --- /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 00000000000..2b56de4bd3f --- /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 00000000000..b1eaaf8543c --- /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 00000000000..90884f1a2a6 --- /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 00000000000..c5fb382c327 --- /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 00000000000..3ed886b4948 --- /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 00000000000..079a4548e78 --- /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 00000000000..2a59b6e33d8 --- /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 00000000000..48d8a6896d6 --- /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 00000000000..55115f8642f --- /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 00000000000..a62bb205039 --- /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 00000000000..f4dfd6c965d --- /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 00000000000..47a1cc91119 --- /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 00000000000..11eaaa7c862 --- /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 00000000000..10f33226acf --- /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 00000000000..28bcef97823 --- /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 00000000000..e3cf4becfb9 --- /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 00000000000..f8bbfe3faa5 --- /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 00000000000..a8a1fbd6514 --- /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 00000000000..8920ce0d4f6 --- /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 00000000000..dc21e322466 --- /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 00000000000..617e8d2ea79 --- /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 00000000000..b08d445191e --- /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 00000000000..e08c79d78f3 --- /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 00000000000..d3f6a1dd3a0 --- /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 00000000000..aa0a69435a6 --- /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 00000000000..1d570ffead7 --- /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 00000000000..6d4629e748a --- /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 00000000000..1b6103f57ae --- /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 00000000000..0d597bbdc22 --- /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 00000000000..2e4f273a010 --- /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 00000000000..bf3baec7a52 --- /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 00000000000..6e922c16c29 --- /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 00000000000..14099965e3f --- /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 00000000000..3def48cca96 --- /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 00000000000..49f53ae5bc3 --- /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 00000000000..efc42772f88 --- /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 00000000000..8829fff3d04 --- /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 00000000000..9259dd6f169 --- /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 00000000000..7b47d605350 --- /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 00000000000..95f4110a2d7 --- /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 00000000000..5758daf7b1f --- /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 00000000000..654ba3de4f7 --- /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 00000000000..eba4710d8a2 --- /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 00000000000..b4bd028d6f0 --- /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 00000000000..6bb46791127 --- /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 00000000000..ccea1294505 --- /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 00000000000..467f4e1ad49 --- /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 00000000000..9740707ae70 --- /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 00000000000..2034f39e9bc --- /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 00000000000..303c649bc8c --- /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 00000000000..1a714a89d48 --- /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 00000000000..b67467b322f --- /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 00000000000..a4a1133b710 --- /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 00000000000..87274ce407f --- /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 00000000000..8735aa82342 --- /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 00000000000..2bfe754f86c --- /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 00000000000..bf8cbcd5a01 --- /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 00000000000..52ee7b31054 --- /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 00000000000..2af8475d17e --- /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 00000000000..166a0a8606a --- /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 00000000000..298e0296fe6 --- /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 00000000000..784993ca793 --- /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 00000000000..de910e79e73 --- /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 00000000000..6a0d27529d8 --- /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 00000000000..862bf36f07c --- /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 00000000000..17b91ce2b3b --- /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 00000000000..6eb662363f7 --- /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 00000000000..0b7130015f1 --- /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 00000000000..e660ce36ef7 --- /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 2bfed05cd17..9c2ed30c527 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 5820fa80354..b7b9c15fe58 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; -- GitLab From c66f7bd423608c219e832bfc5e77aa3a57448df2 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 23 Aug 2024 12:51:14 +0200 Subject: [PATCH 072/217] 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 00000000000..f838eaa29a7 --- /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 073/217] 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 b7fa2bed0ed..b912d8e005c 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 336ba1af4dd..c47a4bdeb6a 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" -- GitLab From e4ffba6ef5d05937a00884a9c56b56baa3bfb8e6 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 23 Aug 2024 14:20:15 +0200 Subject: [PATCH 074/217] [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 bf105b14040..c36313a1476 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 fcdddf199dc..788a13e82b1 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 42e1042717d..f7a288d649a 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 5522a325f19..d0a7ed25363 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 9c05dae979d..dc6c917c6d0 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 386342d7ea5..1033387b527 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)) } -- GitLab From 4057ccd7a37396bc1c6d1742f418415af61b2787 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 23 Aug 2024 16:00:36 +0300 Subject: [PATCH 075/217] 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 00000000000..d0a32bec742 --- /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 a25db4f789d..616dd316887 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 2b50c85602d..f2d7488b2c3 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 2c9799a9d83..ad3a9461c93 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 52870d5ba15..49e97ab11c3 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 } -- GitLab From d26d7f1da6337789b047e0b3374343d793f18b2d Mon Sep 17 00:00:00 2001 From: eskimor Date: Fri, 23 Aug 2024 16:01:38 +0200 Subject: [PATCH 076/217] 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 e96ed6af73d..1e90338a0f1 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 { -- GitLab 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 077/217] 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 9bdf64c9979..ee5ac31e9ca 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 2b8b90ef19a..bc12dd474b0 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: -- GitLab From 13d43bb7c4f68bb0d1cdddf25894d8aedd069adb Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sun, 25 Aug 2024 01:35:50 +0300 Subject: [PATCH 078/217] 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 00000000000..6adc34d71ad --- /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 3740047e027..061e5dbb6c7 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 { -- GitLab 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 079/217] 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 17dc1a552c2..9d7e62af3c6 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 00000000000..83d6d81e252 --- /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 -- GitLab From 91b5a49a3bfc5eabf92936de950690ce2557ed09 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Mon, 26 Aug 2024 00:20:25 +0200 Subject: [PATCH 080/217] 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 00000000000..3dd90d21369 --- /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 00000000000..63b2a0dc1ab --- /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 00000000000..0f645512e8e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +docs/contributor/CONTRIBUTING.md \ No newline at end of file -- GitLab 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 081/217] 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 00000000000..9f83c113686 --- /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 7cdf90877df..06e0756fc96 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 20376431060..c0b412e8ad5 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) => { -- GitLab From ad0de7495e0e4685408d70c755cddaa14e3b02ca Mon Sep 17 00:00:00 2001 From: Muharem Date: Mon, 26 Aug 2024 15:23:20 +0200 Subject: [PATCH 082/217] `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 00000000000..74d477f8e15 --- /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 e1c1be9b133..ac373a351d3 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 a423656c394..f635ed32a12 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 eb63ea59f6c..2b8e4370738 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 9b7c86971bb..c67755e133b 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. -- GitLab From 3cbefaf197b5a5633b61d724f1b9a92fb7790ebb Mon Sep 17 00:00:00 2001 From: Egor_P Date: Mon, 26 Aug 2024 16:44:03 +0200 Subject: [PATCH 083/217] 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 6d31ca7a736..4343dbf915a 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 e1dc42afc6e..262203f0500 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 98df41090a4..0143c09036d 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 2f244a07e8f..77130ff846b 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 f85861bdaa5..6d0fbd7d5c6 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 a9381501359..1c9d8c0207b 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 43fc9083937..e98508ea02e 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 1fcebb3f16a..dfa75b8d3cf 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 2920bc428d9..07d133c80be 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 07a4332800d..5029c82f971 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 d20b62a557b..09b4ef679d2 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 a732bec2352..c676587b1de 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 20c7e691ebc..ab7dd04bb78 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 a0ad248bb70..9c905c87627 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 50970965e11..4aaaf94da58 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 4226595cd2f..83c0eb037f4 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 00000000000..a72ca303aac --- /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 -- GitLab From dd1aaa4713b93607804bed8adeaed6f98f3e5aef Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 26 Aug 2024 18:37:51 +0300 Subject: [PATCH 084/217] 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 00000000000..ccd319cad24 --- /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 f5e042103ea..2decd767478 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 d44364539a2..0b0e13dc08b 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 616dd316887..4a57d61df45 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 f4bc58afd4f..08a2b36118a 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 49e97ab11c3..21e47404862 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 ff229863a68..6f06f238fe3 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 4cc3e9f3a68..99405c2e5f0 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 e8b8c890036..c3403fe1e5f 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 b5aeb162e9f..f84f353fb4a 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 9edd68c2ed9..4244c49bf7f 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 ed108a3102b..5fb304edd7e 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 { -- GitLab From b34d4a083f71245794604fd9fa6464e714f958b1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 26 Aug 2024 21:53:56 +0200 Subject: [PATCH 085/217] [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 b7b2e6f970f..ba7def20fcb 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 d9d918b44a2..4d521db90a4 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') }} -- GitLab From 6ecbde331ead4600536df2fba912a868ebc06625 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Tue, 27 Aug 2024 08:14:01 +0800 Subject: [PATCH 086/217] 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 00000000000..a94bf7aaeba --- /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 31ad0781035..a241041968f 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); } -- GitLab From 5a84374f6bbe37b3c89043923375ab2bca05556d Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Tue, 27 Aug 2024 10:22:35 +0200 Subject: [PATCH 087/217] 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 3dd90d21369..00000000000 --- 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 00000000000..7b6b3498755 --- /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 9612ab6d8d3..99455c98507 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) -- GitLab From f0323d529615de0de121a87eb1c8c6da82bd0ff8 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Tue, 27 Aug 2024 18:28:52 +0900 Subject: [PATCH 088/217] 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 9e0a68d09a1..bf136dc0644 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 7bea72224b8..b4d118aadf0 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 51c6e83c113..548231966e4 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 7fb96de7a4e..348939de1f1 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 00000000000..00fa48de0e2 --- /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 -- GitLab 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 089/217] 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 a749c86faa0..c5d214ec68a 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 -- GitLab From 7e7c33453eeb14f47c6c4d0f98cc982e485edc77 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 27 Aug 2024 12:05:15 +0200 Subject: [PATCH 090/217] 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 89cc7cfa8d4..35e46da1b47 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 00000000000..57f20b3585b --- /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 cfbbab4df7c..47191981520 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 89ef2a48e01..e2ffca8b471 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 00000000000..29bfaeb6450 --- /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 f0159f4307d..19177ed549b 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 a8893b5a79a..ef3450add8e 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(); +} -- GitLab From 0d7becf23b95f66d38676a31d2493c569a98278d Mon Sep 17 00:00:00 2001 From: Parth Date: Tue, 27 Aug 2024 19:08:36 +0400 Subject: [PATCH 091/217] 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 d09962ef2b4..19df1f8c360 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 00000000000..2634c255e16 --- /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 -- GitLab 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 092/217] 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 5d2413ac1e0..ff38192906d 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 35ac9aa6aa9..aacfdb02578 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 553ff1bb3e4..7e1e90f6a8c 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 9059d525689..08c5bd33b4b 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": [ [ -- GitLab 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 093/217] 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 01e07cb395a..47e2d8572c3 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 0f9583cd0eb..d843483b79f 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 02d60538a73..0be1e0a23ca 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 1fbc0689da8..b70cfe3841b 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 00000000000..7fcf3067fab --- /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 -- GitLab From 38fce08896ee595cb8572923bd0d9f40570e7cb6 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 28 Aug 2024 10:21:18 +0300 Subject: [PATCH 094/217] 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 b9f8e8f31e8..00000000000 --- 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 eb10a43ffbe..00000000000 --- 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 de9c6a889ed..cd704b5bd93 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())); } -- GitLab 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 095/217] 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 1de9fcd559d..b7bb6f0ce49 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 9b7a6fafcd1..b8962f0e07a 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 bc12dd474b0..3e94eb77c7b 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 00000000000..0fd396be06a --- /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 40864085349..955e79008c8 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. -- GitLab 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 096/217] 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 a18e667253d..41418bcc511 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), -- GitLab From 09254eb9f8670f942c68914ff6fc0ffb503aaf23 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Wed, 28 Aug 2024 12:12:50 +0200 Subject: [PATCH 097/217] 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 35e46da1b47..8bf4c356397 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 275efe1df63..a181a935ea0 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 a7b2eb19de8..564d7b58c94 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 2aa2b10fbb6..31eb2cf8806 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 283a73d931d..d156ebe2bd6 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 3b9ae6bd445..057b8af9af5 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 37ca27542cb..739c2d4bda1 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 503c2f24fd5..bc0fe9090d3 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 e1f42e1ca86..a907d310c10 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 eb0133b6e8f..0007df908e2 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 00000000000..5ce4303bcf7 --- /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 d45713db522..8fdcc7261b5 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 d85998e3c87..02f5d9a4a70 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 c55e03ee9d6..988502bb2bf 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 b5819d03447..cd245dc0155 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 c1288b502c9..7245b46e2f7 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 406d1fb264d..7c235847761 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 f07223ec6a7..977b57ba065 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 00000000000..d0ec1fd0044 --- /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 a3e811baecf..338d71a4325 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 83477d223dd..ab58f686627 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 a41b1429908..99f98b81261 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 cfc56f4130a..c39d6c68355 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 5bee6df73ba..5c6208ff5d5 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 451ebdf7fc0..cc580fc0e7c 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 469e22d2b3f..4dd5b066d49 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 c0847de89d2..7e0657db8d1 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 e38e383c4c1..ee62387b6bd 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 39ec52cbea0..eee58f506e1 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 c38fa8f3d81..2d6d48e1ddb 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 7e3d3bf55b7..b39c3fdda67 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 0bae16b113d..ca74c2371c2 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 d9d943c7c1f..5b4a4bf22b9 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 1ec7895e308..ae21895de38 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 4a8f4b3ec63..6fe28a3873e 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 2fc21a238bc..731f4df2f6f 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 6bcb3e7863c..bde60960eaf 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 424ef72b694..3ccda760c3f 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 8f09d48e8a5..ff691af7de4 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 b40d0341e32..33e4ac2f4c8 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 66167386605..af6bc1ba58c 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 7758fac7fab..41f22c2dc96 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 c9a41e25eda..d8989b3e1be 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 86df46f63a0..eef79507034 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 e99135aec38..81aa50f7343 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 8c7510c64cb..e0752749bcb 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 03967c63523..bfef0e429ce 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 e04f80a7b9e..990f81ba551 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 d9a91e715fd..9a6c3fde37d 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 f8d8511f271..8387f818e68 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 5fb304edd7e..9e8aed39c89 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 c0333bb7dac..c455d8d39b7 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 8cb7b785bc7..9fcaa53a35d 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 da5073ea687..c6bd89d2337 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 451e7b21dd0..f51dfe1a1ac 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 decb9d6c636..d362ae36d23 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 eba7fdcdae7..f346ae4386a 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 bb52b974f0c..4937469e11e 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 88f2710a5cb..b46b6ecfde1 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 9dd1b144d22..8f74c6b3cb5 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 fe2b6ca72ed..1fc6eb0127e 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 7eef9766b10..2b43ecfa1ce 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) }) }; -- GitLab 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 098/217] 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 73e104b3829..806231db764 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, -- GitLab From 9cf5e8105f0142abf5302bf9be9cffb0106731bf Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Wed, 28 Aug 2024 15:08:08 +0200 Subject: [PATCH 099/217] 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 8bf4c356397..885e2b4b4cf 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 a181a935ea0..9d39962f897 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 beb03b9381d..4c25566607d 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 ce91d48bf58..bb760ae03f4 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 424758c32b3..adc1c1a8efb 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 c45f0126337..195d1b12447 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 00000000000..373522eea1c --- /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 5b9cc89c530..e1ef6de86f9 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 c262d74fa8a..6310e16d5a1 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 6b0751571cc..6cb6447d8fd 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 1b668c061f8..9e54acdace7 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 235d75a4b75..e5fb15cdd07 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 326f3530e26..81cdd40d1bc 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 4149c4880cc..d2e759640c7 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 96ea8f4235d..9d0310fd22e 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 c6bd89d2337..956efca3453 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 fa7686e0109..47ab77ae296 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 5b53b0f80ac..0646460acef 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 22726b7eb9a..54107df75a3 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 504be1ccc36..b09ea1fab23 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 3cf7d98311e..8f36da5bf83 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 f51dfe1a1ac..64497c4bcac 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 d362ae36d23..ce3afe1e6b7 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 9d231fe7d7d..9a02d4daeaa 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 92b90ad4412..b8a8614932a 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 5d3cf8492e5..49ddf3987e9 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 e6f92757e22..2cb2966b5d8 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 08ad537ecdd..474d9ddfb9e 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 9c2ed30c527..6d380a4bcbb 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"] -- GitLab From 97fa922c85976523f755acdd104fdc77ed63dae9 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Wed, 28 Aug 2024 23:42:37 +0900 Subject: [PATCH 100/217] 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 838ba29a621..3d2360d63a7 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); } } -- GitLab From c4ced11fcc728f51011ddef2602d825df8a9becb Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 28 Aug 2024 20:40:21 +0300 Subject: [PATCH 101/217] 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 885e2b4b4cf..07295630b12 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 09bde034cf2..066cbfae53a 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 31eb2cf8806..15d21235d1a 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 7f915b729e0..320511ece5e 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 00000000000..e2826826d40 --- /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 89bc7511dac..907f09263fc 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 d156ebe2bd6..a4e157e8721 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 c64eda12d5e..bddbb0a85d0 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 00000000000..55e042aed87 --- /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 00000000000..9cfdcb22451 --- /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 f2b8b4d562b..00000000000 --- 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 eb6d3fafa3d..00000000000 --- 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 29e2736b06f..02aa867d70f 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 00000000000..442b87b5d77 --- /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 fc164a9d890..6aa2f656a48 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 057b8af9af5..303ec1e3b29 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 cd704b5bd93..82aec951704 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 00000000000..e4401f2406c --- /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 -- GitLab From edd6f7176c33e6ba4ac6f7ed2ca7f78b6eb508d8 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 28 Aug 2024 22:12:06 +0300 Subject: [PATCH 102/217] 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 9d7a8b93119..070bfc8472d 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 bd467a410d0..158dfd73b1a 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 8c3130e4fd9..61738a21e38 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 fd12b57f533..00000000000 --- 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 07b91481dc7..00000000000 --- 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 a6b11fc2405..00000000000 --- 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 00000000000..897b79eeff2 --- /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 00000000000..8fad38f2205 --- /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 + -- GitLab 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 103/217] 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 07295630b12..17df8d63a3d 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 9d39962f897..ebcaa0867fa 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 } -- GitLab 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 104/217] 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 17df8d63a3d..bb0f01542d3 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 ebcaa0867fa..f26a894960a 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" } -- GitLab From 4096ad7f8f769f740cb43669b4e50feb722d19dc Mon Sep 17 00:00:00 2001 From: Pankaj Date: Thu, 29 Aug 2024 00:48:36 +0530 Subject: [PATCH 105/217] 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 00000000000..0957f85b662 --- /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 63978c94e68..e63febb5798 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 3954489a2d1..edb39f23064 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 f38a06f1fdf..a895ea8151a 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), -- GitLab 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 106/217] 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 4128f86fb7c..00000000000 --- 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 fec8d37bb9e..00000000000 --- 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 ac879f44375..00000000000 --- 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 fc37a17ac54..00000000000 --- 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 afdcf4c1b7b..00000000000 --- 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 3a08b9a5fb2..00000000000 --- 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 c610f4066a8..00000000000 --- 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 860177adc87..00000000000 --- 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 80c96b0ef53..3dd5b111481 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 00000000000..45a3acd3f16 --- /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 e3e382af619..0263626d832 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 2e2d7a7fb4f..53f42b9ae4f 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 00000000000..2bb9bd7e7d5 --- /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 00000000000..ebfdca59cae --- /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 e5512e26bba..00000000000 --- 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 2f4ef7ec6a1..00000000000 --- 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 252e4c86480..00000000000 --- 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 f4c2a35c6b6..00000000000 --- 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 e6908045ddb..00000000000 --- 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 ac52e00140e..00000000000 --- 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 eeb18cdd8bb..00000000000 --- 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 c4cca8b4c12..00000000000 --- 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 15eac31e3a4..00000000000 --- 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 b5d8a521993..00000000000 --- 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 d363e51e404..00000000000 --- 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 -- GitLab From 1c4141abeb4c581e503f07af2a3522e6918db591 Mon Sep 17 00:00:00 2001 From: Tarek Mohamed Abdalla Date: Wed, 28 Aug 2024 22:26:29 +0300 Subject: [PATCH 107/217] 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 00000000000..e3037b66fbc --- /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 5740f8081c0..c825300218d 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(), }; -- GitLab From 56201964f9184004ca17992a3b4778de855b1a35 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 28 Aug 2024 23:32:16 +0200 Subject: [PATCH 108/217] 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 4d521db90a4..15eb32f4062 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 00000000000..4c63297efc1 --- /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 00000000000..0b4a97e6f66 --- /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 653e6a2a3e9..bea36741135 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 109/217] 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 b2a67ee8dd2..59afff359d0 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 c5284ba1dd1..88b90e792e7 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 37809396a82..3e84c132aa2 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 a4440a1c6e0..f01440ea02b 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 31713755b9b..dfc41b15bb1 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 cd3f4689f56..020f8e22594 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 c00966fb804..b7b3d12d4d9 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 11665953bd8..ac3f9e679f8 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 519c7dcde54..e8fe11615d7 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 cb6e2c85e36..1e7b01bc472 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 74dd55cc3f2..32f6f28f242 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 00000000000..d309f4e7266 --- /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 -- GitLab 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 110/217] [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 054c7d786ca..ad9d0d1a959 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 1be2dd7921e..25761fb94fd 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 7f2babc6bd4..c51d0ce5627 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 3e94eb77c7b..64bef8d6e00 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 319c95ad611..85f6e8dc780 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: -- GitLab 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 111/217] 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 00000000000..75b431c941d --- /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 eb3b3d3a449..dfa46fcf257 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())) -- GitLab From ba48e4b8cddaf4be4a618b37390fcf6175017daa Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 29 Aug 2024 14:08:25 +0200 Subject: [PATCH 112/217] [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 4c63297efc1..72da40f1983 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 != '' }} -- GitLab From ddd58c15ada7570189a269289ba92778c60ea24a Mon Sep 17 00:00:00 2001 From: ordian Date: Thu, 29 Aug 2024 14:12:44 +0200 Subject: [PATCH 113/217] 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 bf663b4cfce..903c8dd1af2 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 59afff359d0..65e56881c31 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 54e112d1b8b..03580e11b8e 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 8b01a755c3c..b149404b41b 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 169e858deda..978ef718ea4 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 115eee97553..fbf13339dfc 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 3ead456cde5..95fd66bf8e4 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 fbe9ebf809b..75c9e3a5c9b 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 a4c404de2a6..5048656e636 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 9d27e86ef90..bd8d08a842c 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 59fbb094837..ad89e68e905 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 3e84c132aa2..81c926a90e0 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 da1b7a0dad9..4c6ce883557 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 25909beb6a0..36a4c5c24c9 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 00000000000..e6d7142cabd --- /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 -- GitLab From 61bfcb846c2582e9fdf5068f91a20c107d6f030e Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Thu, 29 Aug 2024 13:28:34 +0100 Subject: [PATCH 114/217] 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 00000000000..ae185dc250f --- /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 44e3463dc9f..177c5da74d4 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(()) -- GitLab 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 115/217] [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 b8962f0e07a..b93a07bf61f 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 c51d0ce5627..56d426218c8 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 64bef8d6e00..2212c1aeb0a 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" -- GitLab 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 116/217] 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 bb0f01542d3..8eb4fa5b4e8 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 f26a894960a..7cd18bc8a59 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 9384f8142a9..ae798cf2640 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 228fba41c46..8d20f1f49c0 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 153efdda405..0f68b905b4c 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(); -- GitLab From 18e950407985665a0c260618916ef4517d34bbac Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Fri, 30 Aug 2024 11:37:42 +0200 Subject: [PATCH 117/217] 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 00000000000..0bbc7112bd1 --- /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 00000000000..a3b02667940 --- /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 -- GitLab From 3d4a3355f0d31aeab7b79c23d6b269b5ea669f75 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 30 Aug 2024 17:48:05 +0800 Subject: [PATCH 118/217] 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 00000000000..d78e9ac4693 --- /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 4a57d61df45..4b6ccb08583 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; }, }; -- GitLab 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 119/217] 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 d38516a4ff7..50adbddea41 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 5bcd47a2434..f276321c87e 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 ecb1f3a476e..b8b5f159e71 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 5c201542eb5..401525d02f1 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 f5c9726f3f6..cbf00a9e119 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 109c29f520c..f9c2d0ddbae 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 a3b2636d2ff..56a54f6316c 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 f930476a554..d377a75f106 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 92f2cd18905..4bab4e80fe5 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 2c9ec8db377..2f9d3ed7b4f 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 b6cd73f412b..b608bb416ff 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 bd53fd843c6..8add51b0752 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 73736fd4a3d..493f9fb5ba9 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 a729d8cee4c..b6928e37206 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 27296213e61..1429b0c326a 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 19df1f8c360..30fe95883e7 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 6b77655687f..111b1a19996 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 732b7541738..6e4f99aa3d8 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 88b90e792e7..1742e91276d 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 ad89e68e905..e34055bfa9f 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 f3866146e81..5f80114b596 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 3e81ca49871..aec0ff56e67 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 67dcd6cd7a5..17792cff186 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; -- GitLab From 9cdf3d999e9de7996495c891af7085f55acc695a Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Fri, 30 Aug 2024 20:55:57 +0800 Subject: [PATCH 120/217] 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 fbf13339dfc..b1d22996fd1 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 00000000000..0eda8b02cde --- /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 -- GitLab 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 121/217] 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 dac46cf435a..7a742751005 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 00000000000..97346395319 --- /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 00000000000..a1f380c4712 --- /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 -- GitLab 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 122/217] 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 8eb4fa5b4e8..c878bd60cfb 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 00000000000..d70b8d5b9c1 --- /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 3cffb69daac..60040fda992 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 0143c09036d..47e0983a415 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 00000000000..41b7e622b1b --- /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 4c7356707ab..896edd012dd 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 d10b5e7d3af..56f4868371c 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 f6bf6375a35..233ae986696 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 00000000000..7590d899236 --- /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 -- GitLab 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 123/217] 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 c878bd60cfb..3eaa7c24e30 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 00000000000..9ca230e3e76 --- /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 2cbac305b4d..b33609464fc 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 3fa91c02616..cd02bca466f 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 939fa245d2a..45c77d18e81 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 f5d5d3e6302..243db1b6dde 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 00000000000..80b763d5bd8 --- /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 55714a12481..83ae15700a9 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; -- GitLab From 824e1cfa62d91635cbe3db13b2839690e3635d49 Mon Sep 17 00:00:00 2001 From: Jan-Jan Date: Fri, 30 Aug 2024 21:46:07 +0200 Subject: [PATCH 124/217] '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 006d0be7ded..fcfaab00e55 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 a65aac324f0..a5af29c9c31 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(()) -- GitLab 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 125/217] 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 3eaa7c24e30..d81a9f23e81 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 7cd18bc8a59..24694b185ae 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" } -- GitLab 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 126/217] 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 4fc5b97caae..34335822121 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 -- GitLab 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 127/217] 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 7a742751005..dfdf771a610 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 { -- GitLab 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 128/217] 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 d81a9f23e81..4cb69d1a178 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 24694b185ae..f8c430f5825 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 } -- GitLab 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 129/217] [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 4cb69d1a178..9d8f4cef142 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 41418bcc511..0b03f1127ee 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 96eb25626de..3774edc6998 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 fb9d281e43b..669b6001538 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 3ddef1e01c4..b163d718eb2 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 d4b6855a44d..942922cba6d 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 59a46181005..16e231aa1a2 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 b912449baa4..7126f209a94 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 1bd3d51b5c9..51478dfa4a4 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 a1bdc47e9fb..971b6de5f8f 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 60c7f2f6d3b..10553c35296 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 1ca571721ea..4ee9320e0e4 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 baaff9c7c9f..26a6a907e32 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 cd642bf16ff..7185205f905 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 00000000000..0a1a0ee2367 --- /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 66883b33367..79f4cfa9e0b 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 5c3e7e85a17..465aae23c90 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 add5d239364..0b57ff6e395 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 ca58875c813..4b2b9169682 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 6d3e7dd92db..da25a3bf3b7 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 77ba80d4b2b..709d56d52f0 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 404df2f8c65..f05d061f3fd 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 d067ca46801..854a9da158b 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 c987b7fe5be..e09418c7d5a 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 9b4082c49e2..40394412d81 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 c82d89d2d87..317f339ddd4 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 00000000000..9935652dc51 --- /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 -- GitLab From 5291412e159d3b99c64d5f7f969dbde39d715769 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Mon, 2 Sep 2024 12:47:13 +0200 Subject: [PATCH 130/217] 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 9d8f4cef142..c2cbb0f6d4f 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 d20e059f9fe..2876474e094 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 38c94b34aa2..2c34b7e96f5 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 91793d33f30..92dfa30f2e8 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 87a090bf1ae..f4fe1478f3e 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 70dde03d75a..faff5f7660c 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 a887ee6a532..f568fb4101d 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 49dfe8d5839..975bacea7b4 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 59f63d38059..53b6939298d 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 2d1914e059b..f263baf4bef 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 d61381d3f50..bc5d07f552b 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 c6740269339..fb66f0de232 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 4bb593f9892..deda5fa4ab9 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 e16629302be..96338b64558 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 bf39c02a3f5..7d19c0ed8d8 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 d0c421bccaf..99aadb33b84 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 fa94ee5f1ca..3108068686f 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 63b113bc250..5bcbbd3466e 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 74561e931e7..a8110ca3d19 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 00000000000..db1003ab403 --- /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 -- GitLab From da6541030f72da5a8c5a60c2ab2dd2c2c136b471 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 2 Sep 2024 16:57:07 +0300 Subject: [PATCH 131/217] 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 732a242e729..e65a78f16d7 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 025ac7a81a2..afe83a2a12f 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 15d21235d1a..349dc01d8a4 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 55e042aed87..8e19cf304b0 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 739c2d4bda1..220b0449f33 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 bc0fe9090d3..a600dcce3d6 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 a907d310c10..ab91c6b9b92 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 a4e58253bb1..b1238788486 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 00000000000..35a72f65fb1 --- /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 9ac9d8b4f67..de883d1051f 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 9a71a4ec585..efec081427f 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 8fdcc7261b5..4f63473f632 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 e0e25707e31..b9e5e55be8e 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 7245b46e2f7..f47baf2644e 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 7c235847761..59238b3307c 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 1bb9fec0e27..4db70f99c80 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 0e12c7a2a2d..cdd63788811 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 b0dbccfa634..9c5834d8d80 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 11b7a7924c4..e2218b37f08 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 9a6c3fde37d..0dc28d1361c 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 8387f818e68..6f65c2e2d81 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 9e8aed39c89..251eef97be8 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 a411a83a784..2bcb83f4197 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 5b9bb537563..d64581480cd 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 ce3afe1e6b7..a42eb10ccec 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 f346ae4386a..fa94d50a8be 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 b46b6ecfde1..fc0bcba4c0d 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 2b43ecfa1ce..7d37c5ce87f 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(); -- GitLab 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 132/217] 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 c2cbb0f6d4f..2074bb1d67a 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 f8c430f5825..5ed5ce2f13c 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 8a7c384dcbe..304cb23bb6a 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 401525d02f1..97bc66d6058 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 74a151c168d..2f4c768b89e 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 ea8fdb0e04f..d3eae9dbba6 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 bdb0647fee6..5b1f8d3223d 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 00000000000..5924f978904 --- /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 -- GitLab 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 133/217] 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 2074bb1d67a..7550aab4a9f 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 5ed5ce2f13c..bc5b3255921 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" } -- GitLab 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 134/217] 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 7550aab4a9f..ef6faa5faae 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 bc5b3255921..6bd401d8e15 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" } -- GitLab From 22100999a6edbc67ae54b4cd0e0242505226b083 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 2 Sep 2024 17:53:49 +0200 Subject: [PATCH 135/217] [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 ef6faa5faae..7634cbc166a 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 36f27b6aa03..b8835d55f0d 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 df75092af6e..dc7e14de28f 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 3f1b506aaae..00000000000 --- 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 a0a9367dd14..2ff6c4c9165 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 b65bb6041d5..ac8b013086b 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 46ed4da0d85..00000000000 --- 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 2f248a7162a..fed0d15cc08 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 bcbd779b44d..e48a5664e31 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 66848ba0e26..23fbd9a2742 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 73af997b995..7920eb93403 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 24a196c1d70..61357e6aa6c 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 17ff2c858a1..644fa64c687 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 08036f0b072..b1812e4caf1 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 f08eb4c5d1a..d964901ba4b 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 c62951b7465..dff4b98fd91 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 33f524030d2..9df318587e3 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 80fd92eb0e5..a78c8680249 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 d38aaf32dc9..b3a4447fb02 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 a9ee1969ae0..8e021c8e5e2 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 7ef4599a93c..65240feb719 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 00000000000..4f5ac1c0a40 --- /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 c36313a1476..b7fe1c7dbb1 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 00000000000..dc9a8119079 --- /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 788a13e82b1..f71240ab7c7 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 a3318833fa6..f35eb24d98c 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 99da019dc08..2caea9813e8 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 f7a288d649a..ceb1744c066 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 0f77eaf2c5a..b67da03a631 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 e2c30ce9aec..bbf6a6600d5 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 27a28546afb..0bf889bcca0 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 ca312d44edf..8a3f905a8f2 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 00000000000..6c9ae1c2968 --- /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 00000000000..ecb575524bb --- /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 6ba3506377d..e1a7abd0ad1 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 00000000000..b6f57cebc30 --- /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 9f559dc13b6..da188eaf5bd 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 2c86ec01f5b..b9627774db1 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 3124787896c..de1d292b7c0 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 f75c409aca4..3693793a3e5 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 7ba90d91dfd..0c965e9e6bf 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 ec7c3b56283..55824f6a7fe 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 c4f9f534c1a..3c4a10f82e7 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 60739460346..860c1a83878 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 3e2c1bb369c..f4806eb55b2 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 b0c8fc6252c..d9a0426feca 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 8fbf61a0a53..fe58b910a94 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 00000000000..2412bb0f3bb --- /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 5bca3ad8e27..b42ae1e267f 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 60b988497fc..02d578386a7 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 00000000000..c9d8b67176a --- /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 0cca32ba9e5..d32e0989dfd 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 00000000000..acf7447adab --- /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 26295dee180..48326bf5c19 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 4a9037342bc..87c8cbe8818 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 00000000000..c8f06ed8cb7 --- /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 00000000000..6d4ca402eb3 --- /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 9984f8ac322..7eb0c562939 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 8eedf8fcc7a..ff62dab078e 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 74fecb9d9f0..67868ff7c7c 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 00000000000..fd7cd45a72c --- /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 142c6e9b089..ec3bf9ca3a0 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 acae2f431bf..366ee7aa948 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 3448e8a4096..34be38bed4a 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 00000000000..5ab8e6cde96 --- /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 436f33db400..1e63c89ecd7 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 117409b37b9..7528f2e5d6c 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 7bfa0d6fde0..8bd9001f2b6 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 c3cf3356184..ba0c51152bd 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 dbedb7a52c7..89123b51ef2 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 932e9ade019..79201a8756f 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 00000000000..fd4fc67822f --- /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 9745011c902..44a90a57d4f 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 969cd73d619..6065c23773e 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 b0f93e5b548..89115cfeee9 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 5631285b3c5..28b0eb0ad52 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 ddb3e416dc3..ef8403ff68e 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 338dda3c633..3786976bed9 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 8104be7af80..e8b797f84fa 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 6c078973fed..f9884ee197b 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 68bbe71ae59..34d5226e90c 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 e52b7020666..28bc5c7f5e8 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 b75fc86d5ee..2c49df3452a 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 4579222a2c6..2ef86f48ecb 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 8b128bb770d..08d8e5e2a4f 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 b681d86d2ae..995499092c3 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 0fd1d72c707..59ca458e666 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 eff4a37b0ce..8ed11505a27 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 7bd91ae6774..51ce5b18005 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 1f98d3ba964..75b61d6a4cd 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 86d4ce3e7ac..d32f9832170 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 6066adec52c..c44f4b010c0 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 7152f1dbc27..981ee5c88b4 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 8f2789eb2f3..559a16379bb 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 a5787885329..86ace7d564e 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 8a674f89c9e..6df51c5f704 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 6ce8ecef0df..b540f55642a 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 3f2038b4bdd..12f05742a08 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 1f2d2c8ece2..44121cbfdaf 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 fc8b772a9c7..c3f81175da2 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 bf894a3baf5..30cc4de3905 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 dee411bea8b..ae05e4223b0 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 896edd012dd..2f3fb6b68c4 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 0a86037391b..00ecf239428 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 3d6ae6ddd1d..7478ba8893c 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 bee6bcdf21c..f6a883c03e9 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 f263baf4bef..fab0141e6d4 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 83f4f9ec3dc..6b0cf87a6f7 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 ebbc000d141..97dbe7c361c 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 21d15c75af5..c0898012e9f 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 f7891aedc49..0aeae318462 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 127bc173c10..98ecd7bd309 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 bc5d07f552b..cc96bd59cb4 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 1c334d6f84f..ad3c450eb37 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 e0b3f70c754..d8676117474 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 6d0fbd7d5c6..9fa1f3b1602 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 83b92918dc4..00d902486c8 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 9880e8a17c2..fc52413a909 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 c65880771e0..100bff5a070 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 4ce57b2e501..a9cc2411a9c 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 dc6c917c6d0..b27bbf4ff6c 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 8eb291ea145..3629d8797bf 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 f8bb983e80a..b7318361c7d 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 9c58072d402..9a9137c1809 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 92368b29212..e9b15024be8 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 e91837af0b2..0daf9087218 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 1c9d8c0207b..67d4eff0f7f 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 be4d40c2275..2d9e8f66427 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 1c26742fd67..5717db456a7 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 fa7efc26048..74bf144ac71 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 b4748f14170..87c5057cf9b 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 60d81dc3082..74be73df140 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 ba434ff2962..16c483a2181 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 81705ee2dc9..491caa38dc5 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 46a0fa7a664..c5f3871c079 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 a66c0f84240..4391b069cf0 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 3ae43075000..9cb24a2b282 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 aac6eb03652..b806b8cdb22 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 00000000000..72e6c697e44 --- /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 44a8646142d..8c048a0d2db 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 0b3463f0df9..b8d6d87051c 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 f2f0ccecba1..72743eaa41d 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 0988528c3f2..82edcacdcab 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 c4b5e5583ba..c343e9b3e09 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 a36a74dbbbc..de117982b26 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 5c1dc492a9d..2940c4e00f4 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 b99c275bd0d..aefbc0dbd0a 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 940aa1b734d..fe75b2b6e72 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 8de3660c223..afed14278d1 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 00000000000..4a5c09c6fa8 --- /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 -- GitLab 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 136/217] 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 34335822121..d13add97d41 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 adbc480c6ba..c2080142f50 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 -- GitLab 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 137/217] 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 7634cbc166a..2d4485c9221 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 c796dc5f7c3..629fa728be3 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 cdef39d5bdf..1f2efdbbb5b 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 e5e1e4d24ef..7938223df23 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 71bcdaffac4..9375aca6ed7 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 8f7ec314ecf..a8cd6cb5f4e 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 b4816ad1507..ddebe99e621 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 b6928e37206..a51ee0bd99b 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 00000000000..8706214b5a0 --- /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 1429b0c326a..57cba85c10d 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 d43cf3317e5..b0f78717dd9 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 cfe373e8cba..a3eec3f9d96 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 65e56881c31..665737afa6c 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 978ef718ea4..cb6329bf88e 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 36a810d341c..2a215d5d595 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 b1d22996fd1..e014529ea11 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 95fd66bf8e4..59114e28be1 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 1742e91276d..fa466de1198 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 bd8d08a842c..84d8299cd29 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 e34055bfa9f..ac42ac1611d 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 69789023221..ead825b38f0 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 4aa381e33b1..a3440f686e9 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 dfc41b15bb1..6b046e19083 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 8e34320d38f..72d024e9a87 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 e8fe11615d7..b02c2d8c671 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 00000000000..b4cf261f33a --- /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 -- GitLab From 383550737edee0b722af8ddf5a20a9c32aa0547d Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 2 Sep 2024 21:10:24 +0200 Subject: [PATCH 138/217] [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 2d4485c9221..0d41be5c9bd 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 00000000000..69e79213fa2 --- /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 ef5c52bf6e6..7ef0779dd9b 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 747c2283e21..6b7542e8920 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 9e54acdace7..db284c7cc06 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 a75aee65c20..73f427650c2 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 32654d59ef1..e8f995cffc7 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 1323c8c5d55..f3d2ece2132 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 a078162d799..15c1124eeae 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 a5356924f24..985df672411 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 2fa11df82d0..dceab813f88 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 d0d3651dfe4..7a0b497079c 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 918a4abe6b2..53b9afba778 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 a2a0e85bcf6..8788542a0c5 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 055760729bd..921543dced0 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 8b74493a1e3..b86e761d53e 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 3ed886b4948..2f3e5ae148c 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 a62bb205039..ef8bc95f009 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 10f33226acf..b0e004018f1 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 1b6103f57ae..54e32130635 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 49f53ae5bc3..678e7a44490 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 5758daf7b1f..f1bd36dcbba 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 654ba3de4f7..c000817a8a3 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 eba4710d8a2..ede3bb69b11 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 b4bd028d6f0..409d53b8a06 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 467f4e1ad49..00e893b94f8 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 9740707ae70..54019a6ba99 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 303c649bc8c..359434b9abd 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 a4a1133b710..98e8879457b 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 87274ce407f..91b7b904d2b 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 8735aa82342..f6ad4c5fc34 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 2bfe754f86c..671efebdf4b 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 bf8cbcd5a01..76b4c98d4cb 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 52ee7b31054..a37e9842a2c 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 166a0a8606a..e3571b7f21e 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 784993ca793..9024390fd24 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 de910e79e73..70d405da989 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)?; -- GitLab From c8015b2e79ad8063701d9be026ed77d4a92e50cc Mon Sep 17 00:00:00 2001 From: Clara van Staden Date: Mon, 2 Sep 2024 21:12:10 +0200 Subject: [PATCH 139/217] 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 6894977c21f..84b1476931c 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 96298d4fa89..be456565d40 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 c16743b75ea..82a3b822447 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 a031676c607..871df6d1e51 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 8f36313e360..b157ad4356b 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 01a762d4b99..6c0486c62fa 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 100bff5a070..14409ce4642 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 0daf9087218..982c9fec663 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 7922d3ed02b..47b6006ed6c 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 00000000000..a0c1bbfd2e4 --- /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 -- GitLab From f6eeca91b6cf0810ca3d8e7ea23988d9510851ba Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Mon, 2 Sep 2024 21:03:02 +0100 Subject: [PATCH 140/217] [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 00000000000..417b0b5a4fd --- /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 14b8d2217eb..e4db87d8be7 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 2dbffef7e5a..48002acb147 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 26533cc7c33..d3f719c6235 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 e89fdb8b320..fac135f135c 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); + }); +} -- GitLab 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 141/217] 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 03a7aa98198..ab742ad5c3a 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 b9cdbd60e95..6f393f267b0 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 -- GitLab 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 142/217] 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 00000000000..4865ec1e338 --- /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 7ef0779dd9b..31584427b3b 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 f1bd36dcbba..c51940ba771 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 409d53b8a06..3ffd53e3561 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 54019a6ba99..649479f7790 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 359434b9abd..9a99b01776c 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 76b4c98d4cb..b17067769c0 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 a37e9842a2c..447d55f0dd8 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 e3571b7f21e..7885d681e48 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 70d405da989..533baf8d2c8 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, ) } -- GitLab From 09331f268b0547a76972a904e3e8b5d02d2d5f8b Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 3 Sep 2024 12:55:46 +0200 Subject: [PATCH 143/217] [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 72da40f1983..1ad68d96a63 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: | -- GitLab 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 144/217] 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 00000000000..c85549b3799 --- /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 b93a07bf61f..5fb9dca38d1 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 ad9d0d1a959..9aebd83282e 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 e5b9db0836f..00000000000 --- 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 7c19b420a6a..c33c782a731 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 8658e92efc8..a5de2173a71 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 44cd1933a9c..5ad9ae9bfb3 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 85f6e8dc780..83af0e6b7b2 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 -- GitLab 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 145/217] 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 00000000000..38eb75affe4 --- /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 1d28d207124..ebb72ed3d15 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 ab5be1f24e5..073ee34a79f 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 38f091471f8..a638a9c7ec5 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); +} -- GitLab 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 146/217] 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 0d41be5c9bd..e37b79cc58e 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 6c0730a56a2..c2deddc5341 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 48d35dd3a55..dc0e9d697b4 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 00000000000..60f43b922c7 --- /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 -- GitLab From 89b41c57d5e070681b23db9fafcbfcceb92f39d9 Mon Sep 17 00:00:00 2001 From: bader y Date: Wed, 4 Sep 2024 06:30:57 -0400 Subject: [PATCH 147/217] 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 e37b79cc58e..83c40e9a4c7 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 39e5993d020..a2e22d1ed1e 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 d08f547bba6..cc273685fcb 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 00000000000..dfcdcd4f15b --- /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 070cf130917..f2fe8cb7e16 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 39fa054b480..9898a582dd1 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 6c679f109a0..629edcf6856 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 00000000000..b957b09f564 --- /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 00000000000..5b1b4e2f04c --- /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 00000000000..ac67aef9334 --- /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 00000000000..7106b4b50dc --- /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 00000000000..0501d6cbe45 --- /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 00000000000..6d127b6c0ac --- /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 00000000000..2bf84281c59 --- /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 00000000000..5aedd5b5c18 --- /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 00000000000..f98be3d7cfb --- /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 00000000000..dde561a594f --- /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 00000000000..d8c558a0ccb --- /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 00000000000..6a390c0d38b --- /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 00000000000..fe1fb889f27 --- /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 00000000000..0501d6cbe45 --- /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 00000000000..2dd38093cfd --- /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 00000000000..f05e3505a2b --- /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 00000000000..cd909bbe3c3 --- /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 00000000000..f553f05f20a --- /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"); +} -- GitLab From 9b28a5453a83e21b71f18401f3662078fbd0de1b Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Wed, 4 Sep 2024 18:50:26 +0800 Subject: [PATCH 148/217] 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 00000000000..2c00714c4f8 --- /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 ba0cbc09d53..eadb26254a1 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), } -- GitLab From db41fab999c758ecaac81f73cc1eaa86e971bcf4 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Wed, 4 Sep 2024 14:13:40 +0200 Subject: [PATCH 149/217] 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 34d657194da..702c853684c 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 -- GitLab From cc3b7bbd19f03e6db8d37c57f00f8e85fd1371fc Mon Sep 17 00:00:00 2001 From: Muharem Date: Wed, 4 Sep 2024 14:57:14 +0200 Subject: [PATCH 150/217] 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 83c40e9a4c7..b3eefe1826c 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 21206d26dd5..dea2eb03db3 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 9133baa6120..d456f5b8c46 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 00000000000..5e43b86a975 --- /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 31584427b3b..001b2273c9b 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 9cd96019781..5442e877902 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 ac373a351d3..4a8b213c645 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 f6810f26f05..59a9d23f7b1 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 7b5df17b60a..baf7cce8437 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 3544a8cddb4..79428689caa 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 5240dc215ff..70ce221f10d 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 cadfbfcbfbf..1a7485b4ab7 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 0a58a92b4c9..9755efaea41 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 {} -- GitLab From de0b6f25725325db0e97379f2d49a41dc8a13c44 Mon Sep 17 00:00:00 2001 From: ordian Date: Wed, 4 Sep 2024 14:59:21 +0200 Subject: [PATCH 151/217] 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 cb6329bf88e..1dac3c92cf1 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 4c6ce883557..5824658383b 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 36a4c5c24c9..28d8aa8ea32 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 00000000000..0c0a4b055f6 --- /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 -- GitLab From 778a9e41f54e8e3f67cc57914100efa4a421ed0a Mon Sep 17 00:00:00 2001 From: Evgeny Snitko Date: Wed, 4 Sep 2024 18:22:57 +0400 Subject: [PATCH 152/217] 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 00000000000..fead9cfe336 --- /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 00000000000..735b727e58b --- /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 -- GitLab From 1cff666b5206c28e3116eea7edb36b65135775a1 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Wed, 4 Sep 2024 11:26:26 -0400 Subject: [PATCH 153/217] 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 b3eefe1826c..508fc2e8922 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 00000000000..4cf6425e73a --- /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 55e9f4b3199..800bf4bd073 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 fd10dee2a7c..ba1ea376972 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 00000000000..688bf81e0d7 --- /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()) + ); + } +} -- GitLab From d5346e72e54b040670b60475b90a3c467f03b08c Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Thu, 5 Sep 2024 03:41:11 -0400 Subject: [PATCH 154/217] 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 b7bb6f0ce49..b1f08de3334 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): -- GitLab From cf330ccd58e62b8033acdd0e7b14f33d7a480c09 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 5 Sep 2024 10:14:50 +0200 Subject: [PATCH 155/217] [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 00000000000..630345b9b5a --- /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 ed981a75467..944ae246c1b 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 73f427650c2..93687441fa5 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 e8f995cffc7..29b77c343fe 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 f3d2ece2132..7cd46849655 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 15c1124eeae..c3204c29281 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 985df672411..a941aa9a342 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 dceab813f88..3b83f208d62 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 c7596e44dab..bb5c1ccbc1d 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 6631af8292f..947247e9cf7 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 7a0b497079c..28d161791e5 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 53b9afba778..d87c2e8cd35 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 8788542a0c5..753490cf26b 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 d03ddab1bc5..9fd155408af 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 921543dced0..c5525423a9e 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 cf3351c52fd..20f8ec3364e 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 b86e761d53e..4959a5e2e0c 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 f5c8681c938..b46d4f7c841 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 90884f1a2a6..a81ffea943d 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 2f3e5ae148c..2efacb4e683 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 ef8bc95f009..7476b7a8366 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 f4dfd6c965d..c15784b7f24 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 b0e004018f1..0e1e4d30e6f 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 28bcef97823..3285aecbe78 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 e3cf4becfb9..75995d7bb8a 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 d3f6a1dd3a0..3e1f2757c27 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 3ffd53e3561..2c528562284 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 00e893b94f8..d1fc0823e03 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 9a99b01776c..393acc8c985 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 91b7b904d2b..ef7ce2db32c 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 7885d681e48..1e94d5cafb8 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 533baf8d2c8..51c72349384 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 6a0d27529d8..7974cc1260e 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 6eb662363f7..f52ea957402 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 0b7130015f1..c8218bb8f73 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()) } } -- GitLab From 5e0ec3e0231b43392f569cc4c685b91cb481a3ec Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 5 Sep 2024 10:47:15 +0200 Subject: [PATCH 156/217] 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 00000000000..b43db33c63b --- /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 57806280914..d5fd545481d 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 -- GitLab 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 157/217] [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 4a13f5318f7..997d7622f0c 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 56d426218c8..5b581c45fb8 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 83af0e6b7b2..ca3a2394fb3 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: -- GitLab From 8d9ebcd51a8021f77d6ae16ef9d6388f55d0598f Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 5 Sep 2024 12:37:25 +0200 Subject: [PATCH 158/217] 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 860c1a83878..7ba524e95b1 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 f4806eb55b2..bb265e1925a 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 d32e0989dfd..aff3526b558 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 bf136dc0644..882dcb68fbb 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 8c4446a925d..732ee94f3e1 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 3ebcb44fa43..e568c79bb6a 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 dace785a535..37fe7f0b59e 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 6451901279b..05d9046ab19 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 03ef780ef03..fc2de89d212 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 8aa9602fcc2..30e0b7c72b0 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 00000000000..e33690939d8 --- /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 -- GitLab From f6fd5bc5a644b2c4ed986e372b43ab8f049b7fc6 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 5 Sep 2024 13:26:46 +0200 Subject: [PATCH 159/217] 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 a488e15c36b..1f47a8ef5b0 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 6dc87858530..35f73c290bf 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")] -- GitLab 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 160/217] 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 320511ece5e..43fb551f80d 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 8e19cf304b0..0c0230296eb 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 62d99122c30..168a645430e 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 ab91c6b9b92..a8e7fc16eb4 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 00000000000..c08f4e7fb8f --- /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 4f63473f632..1b345a23f27 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 7065c9b997e..7bc30ae0221 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 37b35fcb910..4bcdd673455 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 555e848f8cc..ee1d490b854 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 fb9f14c9a4a..0186ca58762 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 cec42b8f245..654eaa6ff13 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 fa94d50a8be..610dbd7a686 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 fc0bcba4c0d..04e20be2bd4 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'.", -- GitLab 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 161/217] 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 9b654f8071a..7a53e812ddf 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 @@ crates.sort() 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 @@ for i in range(0, crates_per_group + overflow_crates): 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 00000000000..824e8c11c2a --- /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 25761fb94fd..ed2ef07736b 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 5b581c45fb8..8b4ca48150b 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 ca3a2394fb3..00a0aa2c977 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 -- GitLab From b9b34fb983dac58ae05b0e1379e20363f6f7c88e Mon Sep 17 00:00:00 2001 From: Evgeny Snitko Date: Thu, 5 Sep 2024 19:27:18 +0400 Subject: [PATCH 162/217] 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 ceceb9e6365..b237c9fe6b0 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 00000000000..ddf0642a404 --- /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 001b2273c9b..6ae04902aa8 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 912cb3e27cd..4e4052b2b56 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 ae230a0209a..caac4107cfb 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; } -- GitLab 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 163/217] 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 824e8c11c2a..2e78f4a34ed 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 8b4ca48150b..43123cdbfc4 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 00a0aa2c977..0879870ae13 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 -- GitLab 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 164/217] /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 63bd6a2795a..1c08b621467 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -11,6 +11,8 @@ _HelpAction = _help._HelpAction 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 @@ parser_ui = subparsers.add_parser('update-ui', help='Updates UI tests') 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 00000000000..4cf1b290915 --- /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 00000000000..87d7ee1dcc2 --- /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 dfdf771a610..79a4f6c3b19 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 45a3acd3f16..102437876da 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 e82c97160ef..55decef273f 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) } } -- GitLab From fdb4554e26ebdd4d729158501a3ddb3c6ebdfb6f Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 6 Sep 2024 16:21:09 +0800 Subject: [PATCH 165/217] 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 00000000000..9d51917db7b --- /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 9770b16871e..4cf66302ec8 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 eadb26254a1..4559a01e57e 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 b532e0d4666..0b591c967e6 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 21e47404862..f29ed1b083e 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 22defd7c551..8b699c7faff 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 fd0c5795cbf..d7386a71a0d 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. -- GitLab From 76df1ae460fb2f9910051e0dac2211ab8d156ced Mon Sep 17 00:00:00 2001 From: Egor_P Date: Fri, 6 Sep 2024 10:29:26 +0200 Subject: [PATCH 166/217] [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 bfb3120ad9b..5361db398ae 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 00000000000..81a3c14edec --- /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 00000000000..c236a66a9fa --- /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" -- GitLab 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 167/217] 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 180c229e744..fe1317a033c 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 b912d8e005c..3de85cbeb4d 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 6a5a7853f9c..c4ce5c7f3fb 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 -- GitLab From 5040b3c2186308a06bad408643a5e475df4cfeeb Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Fri, 6 Sep 2024 13:29:16 +0200 Subject: [PATCH 168/217] 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 103d29e8d26..a9732e93441 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 55282fdf4ee..0dcd84bab6c 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 00000000000..46883c5722c --- /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 -- GitLab 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 169/217] [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 c85549b3799..a01384dc002 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 ebcf6c5fc9b..6325033d214 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 5fb9dca38d1..0a1dbc4790c 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 9aebd83282e..9de879d8367 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 997d7622f0c..7ed67703395 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 2e78f4a34ed..9aa6bf23727 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 -- GitLab 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 170/217] 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 9aa6bf23727..8e8f5770e92 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 -- GitLab 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 171/217] 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 00000000000..f76428bbc8f --- /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 ba1ea376972..260c9a91855 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 71aacf07a76..bb0347badcb 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()) } }}; } -- GitLab 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 172/217] 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 a557e881e26..2529297691e 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 00000000000..dbdc7937b73 --- /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 -- GitLab From 868a36bd186f3ef9535ebf7deceac1b2fab19fcb Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Sun, 8 Sep 2024 23:14:51 +0200 Subject: [PATCH 173/217] [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 00000000000..9a0748e46ba --- /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 6ae04902aa8..caebd63408d 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 6b7542e8920..667328ac2d0 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 ca8e62df604..00000000000 --- 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 4011b8379cb..4606135d980 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 93687441fa5..ee51548879d 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 29b77c343fe..25370459acb 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 7cd46849655..8c8aee96284 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 c3204c29281..330393e706e 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 a941aa9a342..6ab892a6b7a 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 3b83f208d62..eb29fca87c1 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 bb5c1ccbc1d..22d6c5b548d 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 947247e9cf7..abfba282bec 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 28d161791e5..4fa2db0c8c1 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 d87c2e8cd35..e1372e2eb8b 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 753490cf26b..d2efb26e5ce 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 c5525423a9e..95c1bd2aa6c 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 4959a5e2e0c..d381db8e398 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 b46d4f7c841..0d644a4238c 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 9186835d291..5c438c1a75a 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 2b56de4bd3f..7f04ae42765 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 a81ffea943d..c5736850960 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 7476b7a8366..ea74d56867f 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 c15784b7f24..2e70d67d8c7 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 0e1e4d30e6f..524979991ec 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 3e1f2757c27..bfeca9b8b4a 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 c000817a8a3..020a578c3a3 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 2c528562284..8cdd7da5db9 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 649479f7790..016bdec37af 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 393acc8c985..4c6e5cd26a1 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 b67467b322f..00000000000 --- 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 98e8879457b..1b48527d23d 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 ef7ce2db32c..9939de1dfd1 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 f6ad4c5fc34..9d70ddf8587 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 b17067769c0..b17d7628fb8 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 447d55f0dd8..f2944c7932a 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 9024390fd24..5813903326b 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 51c72349384..528b0ababfa 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 7974cc1260e..8913592c13b 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 f52ea957402..101ae9aca46 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 c8218bb8f73..b7b660c4083 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); -- GitLab 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 174/217] 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 508fc2e8922..e8510a4c6a5 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 6bd401d8e15..50c7225c749 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" } -- GitLab 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 175/217] 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 e8510a4c6a5..f6071a97915 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 50c7225c749..174811663de 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" } -- GitLab 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 176/217] 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 f6071a97915..e1b0b3f8f9e 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 174811663de..e2611982a98 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" } -- GitLab From a3eda0a1bdc67fc14efbf64c1f0046d452f55e31 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 9 Sep 2024 10:34:53 +0200 Subject: [PATCH 177/217] [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 ee5ac31e9ca..96f214e9427 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 2212c1aeb0a..7d1f37dddd5 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" -- GitLab From c72f9ab594c8c8ea3f51323de8e1846c75f4a428 Mon Sep 17 00:00:00 2001 From: Nick Vikeras Date: Mon, 9 Sep 2024 05:12:56 -0500 Subject: [PATCH 178/217] 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 00000000000..2bab8ef69f8 --- /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 ca74c2371c2..0472a0a2f63 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 0dc28d1361c..28a76847ac0 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 251eef97be8..babb76f022f 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)), } } -- GitLab 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 179/217] 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 00000000000..3300d557fce --- /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 3ce5b4ff864..ed5544fe55c 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. -- GitLab From 030cb4a71b0b390626a586bfe7117b7c66b4700c Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 9 Sep 2024 16:27:22 +0300 Subject: [PATCH 180/217] 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 00000000000..3cec70de93c --- /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 0b2a3495240..9c9601a912a 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 ba89aede914..c045a393bb2 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 4d7b89f37d8..0fcf96a9636 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 4559a01e57e..72707c306f5 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 8b699c7faff..ce5b92551bf 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, -- GitLab From 2d4e89f091c103813e462bd9cb188473b2673708 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 9 Sep 2024 17:12:51 +0200 Subject: [PATCH 181/217] [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 ba7def20fcb..a6a97008dca 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 00000000000..3a08b9a5fb2 --- /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' -- GitLab From d2e962fc1ec5c33b33ca39029d9a525404048491 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 9 Sep 2024 19:21:51 +0200 Subject: [PATCH 182/217] 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 69311c41dd6..6c8f1ed7a30 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 0c8165af40f..3dddcc10f5d 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 -- GitLab From f3f377f5ec7009036a33a822fdbb8439ca696ca7 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 9 Sep 2024 22:25:42 +0200 Subject: [PATCH 183/217] [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 00000000000..fdd7f5e1b89 --- /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 8cdd7da5db9..8601f5f5354 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 016bdec37af..468f5aa8240 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 4c6e5cd26a1..d1e17fb7b39 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 f2944c7932a..73914c9aae0 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, -- GitLab From aec2b10539251fc20450f8efa453f21dee6b95a1 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Tue, 10 Sep 2024 07:31:11 +0900 Subject: [PATCH 184/217] 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 00000000000..e03b946070a --- /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 f395872c8a8..5dc8dc3146c 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 4e09b86fdde..68c2cb8bd1b 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 d4f58a4c56d..d5ae607d90f 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 00000000000..08b42c29a68 --- /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 00000000000..8f3003c0222 --- /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()); + | +++++++ -- GitLab From 9930d2137fcbc55e32476627ec2e30071b393533 Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi Date: Tue, 10 Sep 2024 11:35:41 +0530 Subject: [PATCH 185/217] 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 e1b0b3f8f9e..66fd0d05cc3 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 cde73c4c518..81c2d9f24f2 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 6f274ed18b6..f300bdc5f2b 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 b4d118aadf0..247de3a29b6 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 2f3fb6b68c4..a4a2554b7af 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 97dbe7c361c..6da2a0bc7b9 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 14409ce4642..6c6e2ec7efd 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 5717db456a7..ddd40dbf60e 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 dea2eb03db3..f22feb70382 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 bf173fb618a..55770515d73 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 25324bf1776..aea2bf232cb 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 a3051e4bf27..218afaab924 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 942e11e0b25..abf13a596a7 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 77bfb99669c..cb9177d0c23 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 3343d2be749..9813c5cb6ac 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 1fe72604d37..f126ee861fa 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 1dfbe2b6c41..fac2d1312c0 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 7d19c0ed8d8..266894c3e4e 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 dff7046f197..34646f84aed 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 274f16ab630..ba0a3487011 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 195d1b12447..5be3a59dc7b 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 6b046e19083..6ec49c5830f 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 72d024e9a87..b0323156911 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 b02c2d8c671..d0c1cd89de3 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 00000000000..860b85a4888 --- /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 caebd63408d..c8409078af5 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 2f22cd42591..d02d0e32180 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 be8344ba79b..77dfc09c880 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 a638a9c7ec5..30a01b93b31 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 eef79507034..6b711f2425e 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 37af683fbc7..677ef4e94c8 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 abacfa7b62c..662b7f1a94b 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 fff848b3b0e..f43ffe3c87e 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 b2cd017e1e2..534ba1e863f 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 d254bf20601..4b5c35562bd 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 3b9afae4ca0..4b25f85fba6 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 35c49360b7f..a3be654547d 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 3671d4aff6b..b4f749c90f5 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 55dea364eef..a9f1c237306 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 514f3bcba20..840081003b8 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 474d9ddfb9e..cce13c48af7 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 83ae15700a9..ccec648ce4c 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 6cbfbb87960..ce38c65479e 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 { -- GitLab 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 186/217] 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 a7498604897..e3fc27f0a94 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 d10f34e6fae..1060d252fd2 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 6c8f1ed7a30..8f60b9cccf8 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 15eb32f4062..2c06df5a509 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 96f214e9427..c936e7c8938 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 9de879d8367..cba7df51742 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 c06beb5e98e..658da4451dc 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 9f96b92e0ce..77653cd43b6 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 bee709a1207..77f04861bff 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 2d91850b82c..195c14dbd5a 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 4343dbf915a..dd6a111d67e 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 c5d214ec68a..72e01a4833e 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 c236a66a9fa..c4c50f5398e 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 930b8da772d..6666c115562 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 50c20563b43..0d2ce78ab78 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 262203f0500..83119dd4ed2 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 ddf0642a404..4c0a2629e41 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 8e8f5770e92..97ad86e3998 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 -- GitLab From 8236718e961cb357d46368946f0544ec8ce6718e Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Tue, 10 Sep 2024 17:07:03 +0800 Subject: [PATCH 187/217] 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 00000000000..168d65970c9 --- /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 f29ed1b083e..cca83a5055c 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)); } } } -- GitLab 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 188/217] [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 6325033d214..ea0a4b94321 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 e9db2d91297..e035a30c7c2 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 97ad86e3998..e4f0d6575c5 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 -- GitLab From d887804b235003282b26d4202f40c79fb302f16c Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 10 Sep 2024 15:28:28 +0200 Subject: [PATCH 189/217] [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 1ad68d96a63..85e7b77801d 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 00000000000..ed9b2b5a735 --- /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 30b8338bc5c..7f8423a0127 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 eed8a22e8e7..7f1ce0556ea 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 48e4d975eb0..81377210eb4 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 2c423f8c28d..21397abc8fc 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 9944927d557..4cba524dbe2 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 36577670a40..94da6748cbd 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 4b5c35562bd..700e212688c 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 00000000000..61f93fcd921 --- /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 00000000000..2466c3ea5d5 --- /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 b05f26ff55d..4ebe8c25a67 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, + } + } +} -- GitLab 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 201/217] [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 168a645430e..2947867c516 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 bc0187bf492..e678118440f 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 b163d718eb2..bf6ea0c9814 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 942922cba6d..2149ce81fa8 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 7126f209a94..65aa4f894c2 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 a8e7fc16eb4..fe96d29c1ce 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 0b57ff6e395..3c071e34fe1 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 f05d061f3fd..9d85039b888 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 00000000000..eb18301b101 --- /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 -- GitLab 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 202/217] 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 c33c782a731..6f9db9da9e6 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 -- GitLab 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 203/217] 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 35dc6192817..d30acc0f5b2 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 07d133c80be..80417ea0036 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 aea2bf232cb..0c9f9461f7f 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 f1050b3ae63..ab3d6704c93 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 00000000000..5f95906f473 --- /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 5029c82f971..25bf777047d 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 218afaab924..614eae895a7 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 f1050b3ae63..ab3d6704c93 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 00000000000..d3edc1a8b20 --- /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 c676587b1de..c969bb2985b 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 cb9177d0c23..9b251a90d67 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 3396a8caea0..dce959e817b 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 00000000000..264213c94d4 --- /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 ab7dd04bb78..64e956d8b6b 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 9813c5cb6ac..07bfba92c93 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 3396a8caea0..dce959e817b 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 00000000000..e962123f216 --- /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 00000000000..154146034e6 --- /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 -- GitLab 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 204/217] 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 d30acc0f5b2..e5b12a60da2 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 6af3f270a90..94d43c5eee2 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 895a984eccb..2640c27d016 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 259be790c3e..28d9da0993f 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 ad3c4fd58da..055bd50d829 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 00000000000..584bce8f1df --- /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 0e78351bce0..bb0387a4b35 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 a8fa905d2e5..d57e7926b0e 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 838ca6eeafb..ac844e0f328 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 00000000000..f61bc4285a0 --- /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 0e78351bce0..bb0387a4b35 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; -- GitLab From 8d0aab812ebb70e39b0af893862ba204dc098860 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 12 Sep 2024 19:26:59 +0200 Subject: [PATCH 205/217] [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 e5b12a60da2..5f0fa0f613f 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 00000000000..a17bacd2fb9 --- /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 db284c7cc06..903298d2df2 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 54e32130635..5548dca66d0 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 0d597bbdc22..85656a57b49 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 9259dd6f169..bd05726a1a4 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 d9257d38b66..02d60ee9e34 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 b7b660c4083..89cf9278644 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 6d380a4bcbb..b7c1c375094 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", ] -- GitLab From 766ab8283ed331b1fc3bbfc8b6737d35b5eb03d4 Mon Sep 17 00:00:00 2001 From: Cyrill Leutwiler Date: Fri, 13 Sep 2024 09:03:40 +0200 Subject: [PATCH 206/217] [pallet-revive] Add balance_of syscyall for fetching foreign balances (#5675) This adds an API method `balance_of`, corresponding to the [BALANCE](https://www.evm.codes/#31?fork=cancun) EVM opcode. In `Ext`, `balance` and `balance_of` are internally routed through the same new `account_balance` method: `balance` is technically the same as `balance_of` with the caller address. This avoids duplicating all the tests and avoids a small inefficiency (in theory, `balance` directly call `balance_of` however this introduces a round trip of converting the target address to a H160 and back). --------- Signed-off-by: Cyrill Leutwiler Signed-off-by: xermicus Co-authored-by: command-bot <> --- prdoc/pr_5675.prdoc | 14 ++++++++ .../revive/fixtures/contracts/balance_of.rs | 36 +++++++++++++++++++ .../frame/revive/src/benchmarking/mod.rs | 20 +++++++++++ substrate/frame/revive/src/exec.rs | 21 +++++++---- substrate/frame/revive/src/tests.rs | 25 +++++++++++++ substrate/frame/revive/src/wasm/runtime.rs | 24 +++++++++++++ substrate/frame/revive/src/weights.rs | 21 +++++++++++ substrate/frame/revive/uapi/src/host.rs | 10 +++++- .../frame/revive/uapi/src/host/riscv32.rs | 5 +++ 9 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 prdoc/pr_5675.prdoc create mode 100644 substrate/frame/revive/fixtures/contracts/balance_of.rs diff --git a/prdoc/pr_5675.prdoc b/prdoc/pr_5675.prdoc new file mode 100644 index 00000000000..ceae446dab1 --- /dev/null +++ b/prdoc/pr_5675.prdoc @@ -0,0 +1,14 @@ +title: "[pallet-revive] Add balance_of syscyall for fetching foreign balances" + +doc: + - audience: Runtime Dev + description: | + This adds an API method balance_of, corresponding to the BALANCE EVM opcode. + +crates: + - name: pallet-revive + bump: minor + - name: pallet-revive-uapi + bump: minor + - name: pallet-revive-fixtures + bump: minor diff --git a/substrate/frame/revive/fixtures/contracts/balance_of.rs b/substrate/frame/revive/fixtures/contracts/balance_of.rs new file mode 100644 index 00000000000..1f94d3e506c --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/balance_of.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, u64_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!(address: &[u8; 20],); + + let reported_free_balance = u64_output!(api::balance_of, address); + + assert_ne!(reported_free_balance, 0); +} diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 8601f5f5354..ed4f8f362e3 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -612,6 +612,26 @@ mod benchmarks { assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().balance()); } + #[benchmark(pov_mode = Measured)] + fn seal_balance_of() { + let len = ::max_encoded_len(); + let account = account::("target", 0, 0); + let address = T::AddressMapper::to_address(&account); + let balance = Pallet::::min_balance() * 2u32.into(); + T::Currency::set_balance(&account, balance); + + build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]); + + let result; + #[block] + { + result = runtime.bench_balance_of(memory.as_mut_slice(), len as u32, 0); + } + + assert_ok!(result); + assert_eq!(U256::from_little_endian(&memory[..len]), runtime.ext().balance_of(&address)); + } + #[benchmark(pov_mode = Measured)] fn seal_value_transferred() { build_runtime!(runtime, memory: [[0u8;32], ]); diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 587d89b3198..b065adb4675 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -305,6 +305,11 @@ pub trait Ext: sealing::Sealed { /// The `value_transferred` is already added. fn balance(&self) -> U256; + /// Returns the balance of the supplied account. + /// + /// The `value_transferred` is already added. + fn balance_of(&self, address: &H160) -> U256; + /// Returns the value transferred along with this call. fn value_transferred(&self) -> U256; @@ -1256,6 +1261,11 @@ where fn allows_reentry(&self, id: &T::AccountId) -> bool { !self.frames().any(|f| &f.account_id == id && !f.allows_reentry) } + + /// Returns the *free* balance of the supplied AccountId. + fn account_balance(&self, who: &T::AccountId) -> U256 { + T::Currency::reducible_balance(who, Preservation::Preserve, Fortitude::Polite).into() + } } impl<'a, T, E> Ext for Stack<'a, T, E> @@ -1496,12 +1506,11 @@ where } fn balance(&self) -> U256 { - T::Currency::reducible_balance( - &self.top_frame().account_id, - Preservation::Preserve, - Fortitude::Polite, - ) - .into() + self.account_balance(&self.top_frame().account_id) + } + + fn balance_of(&self, address: &H160) -> U256 { + self.account_balance(&::AddressMapper::to_account_id(address)) } fn value_transferred(&self) -> U256 { diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 19d6eabd577..80c49849464 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -4063,6 +4063,31 @@ mod run_tests { }); } + #[test] + fn balance_of_api() { + let (wasm, _code_hash) = compile_module("balance_of").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(Ð_ALICE, 1_000_000); + + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract(); + + // The fixture asserts a non-zero returned free balance of the account; + // The ETH_ALICE account is endowed; + // Hence we should not revert + assert_ok!(builder::call(addr).data(ALICE_ADDR.0.to_vec()).build()); + + // The fixture asserts a non-zero returned free balance of the account; + // The ETH_BOB account is not endowed; + // Hence we should revert + assert_err_ignore_postinfo!( + builder::call(addr).data(BOB_ADDR.0.to_vec()).build(), + >::ContractTrapped + ); + }); + } + #[test] fn balance_api_returns_free_balance() { let (wasm, _code_hash) = compile_module("balance").unwrap(); diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 02d60ee9e34..80daac8f9db 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -314,6 +314,8 @@ pub enum RuntimeCosts { GasLeft, /// Weight of calling `seal_balance`. Balance, + /// Weight of calling `seal_balance_of`. + BalanceOf, /// Weight of calling `seal_value_transferred`. ValueTransferred, /// Weight of calling `seal_minimum_balance`. @@ -457,6 +459,7 @@ impl Token for RuntimeCosts { Address => T::WeightInfo::seal_address(), GasLeft => T::WeightInfo::seal_gas_left(), Balance => T::WeightInfo::seal_balance(), + BalanceOf => T::WeightInfo::seal_balance_of(), ValueTransferred => T::WeightInfo::seal_value_transferred(), MinimumBalance => T::WeightInfo::seal_minimum_balance(), BlockNumber => T::WeightInfo::seal_block_number(), @@ -1515,6 +1518,27 @@ pub mod env { )?) } + /// Stores the *free* balance of the supplied address into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::balance`]. + #[api_version(0)] + fn balance_of( + &mut self, + memory: &mut M, + addr_ptr: u32, + out_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::BalanceOf)?; + let mut address = H160::zero(); + memory.read_into_buf(addr_ptr, address.as_bytes_mut())?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(self.ext.balance_of(&address)), + 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)] diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 8913592c13b..b66c28bdf7d 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -69,6 +69,7 @@ pub trait WeightInfo { fn seal_address() -> Weight; fn seal_gas_left() -> Weight; fn seal_balance() -> Weight; + fn seal_balance_of() -> Weight; fn seal_value_transferred() -> Weight; fn seal_minimum_balance() -> Weight; fn seal_block_number() -> Weight; @@ -409,6 +410,16 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 4_361_000 picoseconds. Weight::from_parts(4_577_000, 0) } + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn seal_balance_of() -> Weight { + // Proof Size summary in bytes: + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 3_751_000 picoseconds. + Weight::from_parts(3_874_000, 3517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -1223,6 +1234,16 @@ impl WeightInfo for () { // Minimum execution time: 4_361_000 picoseconds. Weight::from_parts(4_577_000, 0) } + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn seal_balance_of() -> Weight { + // Proof Size summary in bytes: + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 3_751_000 picoseconds. + Weight::from_parts(3_874_000, 3517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index 101ae9aca46..0c9dce3fb38 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -56,13 +56,21 @@ pub trait HostFn: private::Sealed { /// otherwise. fn lock_delegate_dependency(code_hash: &[u8; 32]); - /// Stores the *free* balance of the current account into the supplied buffer. + /// Stores the **reducible** balance of the current account into the supplied buffer. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the balance. fn balance(output: &mut [u8; 32]); + /// Stores the **reducible** balance of the supplied address into the supplied buffer. + /// + /// # Parameters + /// + /// - `addr`: The target address of which to retreive the free balance. + /// - `output`: A reference to the output data buffer to write the balance. + fn balance_of(addr: &[u8; 20], output: &mut [u8; 32]); + /// Stores the current block number of the current contract into the supplied buffer. /// /// # Parameters diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index 89cf9278644..908d4f52520 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -82,6 +82,7 @@ mod sys { 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); + pub fn balance_of(addr_ptr: *const u8, 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); @@ -497,6 +498,10 @@ impl HostFn for HostFnImpl { ret_val.into_bool() } + fn balance_of(address: &[u8; 20], output: &mut [u8; 32]) { + unsafe { sys::balance_of(address.as_ptr(), output.as_mut_ptr()) }; + } + fn caller_is_origin() -> bool { let ret_val = unsafe { sys::caller_is_origin() }; ret_val.into_bool() -- GitLab From 5a29cee02390401916aae5c353e4b28d2e3be90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 13 Sep 2024 09:22:27 +0200 Subject: [PATCH 207/217] revive: Get rid of no longer needed trait bound (#5696) This was necessary when we needed to compact encode a `Option` which is no longer necessary. The storage deposit limit is now non optional. This is just a left over. Also got rid of the `T::Hash: IsType`. We use `keccak256` instead of the generic hash function everywhere now. --- .../revive/src/benchmarking/call_builder.rs | 11 ++--------- substrate/frame/revive/src/benchmarking/mod.rs | 6 +----- substrate/frame/revive/src/exec.rs | 4 +--- substrate/frame/revive/src/lib.rs | 17 +++-------------- substrate/frame/revive/src/storage.rs | 8 ++------ substrate/frame/revive/src/storage/meter.rs | 15 +++------------ .../frame/revive/src/test_utils/builder.rs | 4 ---- substrate/frame/revive/src/wasm/mod.rs | 5 ++--- 8 files changed, 14 insertions(+), 56 deletions(-) diff --git a/substrate/frame/revive/src/benchmarking/call_builder.rs b/substrate/frame/revive/src/benchmarking/call_builder.rs index 020a578c3a3..8a859a3a508 100644 --- a/substrate/frame/revive/src/benchmarking/call_builder.rs +++ b/substrate/frame/revive/src/benchmarking/call_builder.rs @@ -22,14 +22,11 @@ use crate::{ storage::meter::Meter, transient_storage::MeterEntry, wasm::{ApiVersion, PreparedCall, Runtime}, - BalanceOf, Config, DebugBuffer, Error, GasMeter, MomentOf, Origin, TypeInfo, WasmBlob, Weight, + BalanceOf, Config, DebugBuffer, Error, GasMeter, MomentOf, Origin, 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}; +use sp_core::U256; type StackExt<'a, T> = Stack<'a, T, WasmBlob>; @@ -49,9 +46,7 @@ pub struct CallSetup { 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 { @@ -62,8 +57,6 @@ 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, { diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index ed4f8f362e3..332c425d714 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -68,10 +68,8 @@ struct Contract { 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 { @@ -221,12 +219,10 @@ fn default_deposit_limit() -> BalanceOf { #[benchmarks( where - as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, - BalanceOf: From< as Currency>::Balance> + Into + TryFrom, + BalanceOf: Into + TryFrom, T: Config + pallet_balances::Config, MomentOf: Into, ::RuntimeEvent: From>, - T::Hash: IsType, as Currency>::Balance: From>, )] mod benchmarks { diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index b065adb4675..233658696c8 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, IsType, OriginTrait, Time, + Contains, OriginTrait, Time, }, weights::Weight, Blake2_128Concat, BoundedVec, StorageHasher, @@ -699,7 +699,6 @@ impl CachedContract { impl<'a, T, E> Stack<'a, T, E> where T: Config, - T::Hash: IsType, BalanceOf: Into + TryFrom, MomentOf: Into, E: Executable, @@ -1271,7 +1270,6 @@ where impl<'a, T, E> Ext for Stack<'a, T, E> where T: Config, - T::Hash: IsType, E: Executable, BalanceOf: Into + TryFrom, MomentOf: Into, diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index d1e17fb7b39..1cc77a673b1 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -50,8 +50,7 @@ use crate::{ storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, wasm::{CodeInfo, RuntimeCosts, WasmBlob}, }; -use codec::{Codec, Decode, Encode, HasCompact}; -use core::fmt::Debug; +use codec::{Codec, Decode, Encode}; use environmental::*; use frame_support::{ dispatch::{ @@ -597,10 +596,7 @@ pub mod pallet { } #[pallet::hooks] - impl Hooks> for Pallet - where - T::Hash: IsType, - { + impl Hooks> for Pallet { fn on_idle(_block: BlockNumberFor, limit: Weight) -> Weight { let mut meter = WeightMeter::with_limit(limit); ContractInfo::::process_deletion_queue_batch(&mut meter); @@ -734,8 +730,6 @@ 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, { @@ -987,7 +981,6 @@ impl Pallet where BalanceOf: Into + TryFrom, MomentOf: Into, - T::Hash: IsType, { /// A generalized version of [`Self::call`]. /// @@ -1175,11 +1168,7 @@ where } } -impl Pallet -where - T: Config, - T::Hash: IsType, -{ +impl Pallet { /// Return the existential deposit of [`Config::Currency`]. fn min_balance() -> BalanceOf { >>::minimum_balance() diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs index 9939de1dfd1..ef7ce2db32c 100644 --- a/substrate/frame/revive/src/storage.rs +++ b/substrate/frame/revive/src/storage.rs @@ -33,12 +33,11 @@ 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, H256}; +use sp_core::{ConstU32, Get, H160}; use sp_io::KillStorageResult; use sp_runtime::{ traits::{Hash, Saturating, Zero}, @@ -78,10 +77,7 @@ pub struct ContractInfo { delegate_dependencies: DelegateDependencyMap, } -impl ContractInfo -where - T::Hash: IsType, -{ +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 diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs index 9d70ddf8587..a2ece03f9aa 100644 --- a/substrate/frame/revive/src/storage/meter.rs +++ b/substrate/frame/revive/src/storage/meter.rs @@ -27,11 +27,10 @@ use frame_support::{ traits::{ fungible::{Mutate, MutateHold}, tokens::{Fortitude, Fortitude::Polite, Precision, Preservation, Restriction}, - Get, IsType, + Get, }, DefaultNoBound, RuntimeDebugNoBound, }; -use sp_core::H256; use sp_runtime::{ traits::{Saturating, Zero}, DispatchError, FixedPointNumber, FixedU128, @@ -397,12 +396,7 @@ where } /// Functions that only apply to the nested state. -impl RawMeter -where - T: Config, - T::Hash: IsType, - E: Ext, -{ +impl> RawMeter { /// Charges `diff` from the meter. pub fn charge(&mut self, diff: &Diff) { match &mut self.own_contribution { @@ -504,10 +498,7 @@ where } } -impl Ext for ReservingExt -where - T::Hash: IsType, -{ +impl Ext for ReservingExt { 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 b17d7628fb8..d361590df95 100644 --- a/substrate/frame/revive/src/test_utils/builder.rs +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -21,11 +21,8 @@ use crate::{ 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; use sp_core::H160; /// Helper macro to generate a builder for contract API calls. @@ -53,7 +50,6 @@ macro_rules! builder { #[allow(dead_code)] impl $name where - as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, BalanceOf: Into + TryFrom, crate::MomentOf: Into, T::Hash: frame_support::traits::IsType, diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs index 5813903326b..b8f6eef126b 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, IsType}, + traits::{fungible::MutateHold, tokens::Precision::BestEffort}, }; -use sp_core::{Get, H256, U256}; +use sp_core::{Get, U256}; use sp_runtime::DispatchError; /// Validated Wasm module ready for execution. @@ -125,7 +125,6 @@ impl Token for CodeLoadToken { impl WasmBlob where - T::Hash: IsType, BalanceOf: Into + TryFrom, { /// We only check for size and nothing else when the code is uploaded. -- GitLab From d66dee3c3da836bcf41a12ca4e1191faee0b6a5b Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:55:15 +0200 Subject: [PATCH 208/217] [ci] Move check each crate osx from gitlab to github (#5690) PR adds `cargo-check-each-crate-macos` job in GitHub actions. It'll work some time in both CI systems until it's moved entirely to GHA. cc https://github.com/paritytech/ci_cd/issues/1021 --- .github/actions/set-up-mac/README.md | 15 -------- .github/actions/set-up-mac/action.yml | 43 --------------------- .github/workflows/tests-misc.yml | 55 +++++++++++++++------------ 3 files changed, 31 insertions(+), 82 deletions(-) delete mode 100644 .github/actions/set-up-mac/README.md delete 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 deleted file mode 100644 index 0bbc7112bd1..00000000000 --- a/.github/actions/set-up-mac/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# 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 deleted file mode 100644 index a3b02667940..00000000000 --- a/.github/actions/set-up-mac/action.yml +++ /dev/null @@ -1,43 +0,0 @@ -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 diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index e4f0d6575c5..90685743a41 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -346,30 +346,37 @@ jobs: cp .forklift/config.toml /github/home/.forklift/config.toml PYTHONUNBUFFERED=x .github/scripts/check-each-crate.py ${{ matrix.index }} ${{ strategy.job-total }} - # 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 - - # - 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 + cargo-check-all-crate-macos: + timeout-minutes: 30 + runs-on: parity-macos + env: + SKIP_WASM_BUILD: 1 + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Set rust version from env file + run: | + RUST_VERSION=$(cat .github/env | sed -E 's/.*ci-unified:([^-]+)-([^-]+).*/\2/') + echo $RUST_VERSION + echo "RUST_VERSION=${RUST_VERSION}" >> $GITHUB_ENV + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@1ccc07ccd54b6048295516a3eb89b192c35057dc # master from 12.09.2024 + - name: Install rust ${{ env.RUST_VERSION }} + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + cache: false + toolchain: ${{ env.RUST_VERSION }} + target: wasm32-unknown-unknown + components: cargo, clippy, rust-docs, rust-src, rustfmt, rustc, rust-std + - name: Install protobuf + run: brew install protobuf + - name: cargo info + run: | + echo "######## rustup show ########" + rustup show + echo "######## cargo --version ########" + cargo --version + - name: Run cargo check + run: cargo check --workspace --locked confirm-required-test-misc-jobs-passed: runs-on: ubuntu-latest -- GitLab From b21da7480899fcfd5f9b8b6f44d54adb118badbb Mon Sep 17 00:00:00 2001 From: Cyrill Leutwiler Date: Fri, 13 Sep 2024 12:33:51 +0200 Subject: [PATCH 209/217] [pallet-revive] uapi: allow create1 equivalent calls (#5701) The salt argument should be optional to allow create1 equivalent calls. --------- Signed-off-by: xermicus --- prdoc/pr_5701.prdoc | 14 +++++++ .../fixtures/contracts/caller_contract.rs | 22 ++++++++--- .../fixtures/contracts/create1_with_value.rs | 39 +++++++++++++++++++ .../create_storage_and_instantiate.rs | 2 +- .../contracts/destroy_and_transfer.rs | 2 +- .../contracts/instantiate_return_code.rs | 2 +- substrate/frame/revive/src/tests.rs | 22 +++++++++++ substrate/frame/revive/uapi/src/host.rs | 2 +- .../frame/revive/uapi/src/host/riscv32.rs | 5 ++- 9 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 prdoc/pr_5701.prdoc create mode 100644 substrate/frame/revive/fixtures/contracts/create1_with_value.rs diff --git a/prdoc/pr_5701.prdoc b/prdoc/pr_5701.prdoc new file mode 100644 index 00000000000..3d237eb34e7 --- /dev/null +++ b/prdoc/pr_5701.prdoc @@ -0,0 +1,14 @@ +title: "[pallet-revive] uapi: allow create1 equivalent calls" + +doc: + - audience: Runtime Dev + description: | + The salt argument should be optional to allow create1 equivalent calls. + +crates: + - name: pallet-revive + bump: minor + - name: pallet-revive-uapi + bump: major + - name: pallet-revive-fixtures + bump: patch diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs index eb29fca87c1..f9a30b87df4 100644 --- a/substrate/frame/revive/fixtures/contracts/caller_contract.rs +++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs @@ -49,25 +49,35 @@ pub extern "C" fn call() { &reverted_input, None, None, - &salt, + Some(&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 + 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, + &value, + &input, + None, + None, + Some(&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. + 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, + &value, + &input, + None, + None, + Some(&salt), ); assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); @@ -83,7 +93,7 @@ pub extern "C" fn call() { &input, Some(&mut callee), None, - &salt, + Some(&salt), ) .unwrap(); diff --git a/substrate/frame/revive/fixtures/contracts/create1_with_value.rs b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs new file mode 100644 index 00000000000..644777aff99 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs @@ -0,0 +1,39 @@ +// 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, u256_bytes}; +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],); + + let mut value = [0; 32]; + api::value_transferred(&mut value); + + // Deploy the contract with no salt (equivalent to create1). + let ret = api::instantiate(code_hash, 0u64, 0u64, None, &value, &[], None, None, None); + assert!(ret.is_ok()); +} 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 e1372e2eb8b..463706457a1 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs @@ -48,7 +48,7 @@ pub extern "C" fn call() { input, Some(&mut address), None, - &salt, + Some(&salt), ) .unwrap(); diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs index d381db8e398..8342f4acf95 100644 --- a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs +++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs @@ -42,7 +42,7 @@ pub extern "C" fn deploy() { &input, Some(&mut address), None, - &salt, + Some(&salt), ) .unwrap(); diff --git a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs index c5736850960..9764859c619 100644 --- a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs @@ -41,7 +41,7 @@ pub extern "C" fn call() { input, None, None, - &[0u8; 32], // Salt. + Some(&[0u8; 32]), // Salt. ) { Ok(_) => 0u32, Err(code) => code as u32, diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 80c49849464..c7185caf0ef 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -4232,4 +4232,26 @@ mod run_tests { assert_ok!(builder::call(addr_caller).data(addr_callee.encode()).build()); }); } + + #[test] + fn create1_with_value_works() { + let (code, code_hash) = compile_module("create1_with_value").unwrap(); + let value = 42; + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create the contract: Constructor does nothing. + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + // Call the contract: Deploys itself using create1 and the expected value + assert_ok!(builder::call(addr).value(value).data(code_hash.encode()).build()); + + // We should see the expected balance at the expected account + let address = crate::address::create1(&addr, 0); + let account_id = ::AddressMapper::to_account_id(&address); + let usable_balance = ::Currency::usable_balance(&account_id); + assert_eq!(usable_balance, value); + }); + } } diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index 0c9dce3fb38..538de7ea251 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -409,7 +409,7 @@ pub trait HostFn: private::Sealed { input: &[u8], address: Option<&mut [u8; 20]>, output: Option<&mut &mut [u8]>, - salt: &[u8; 32], + salt: Option<&[u8; 32]>, ) -> Result; /// Checks whether a specified address belongs to a contract. diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index 908d4f52520..0bb0ede4543 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -198,7 +198,7 @@ impl HostFn for HostFnImpl { input: &[u8], mut address: Option<&mut [u8; 20]>, mut output: Option<&mut &mut [u8]>, - salt: &[u8; 32], + salt: Option<&[u8; 32]>, ) -> Result { let address = match address { Some(ref mut data) => data.as_mut_ptr(), @@ -206,6 +206,7 @@ impl HostFn for HostFnImpl { }; let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); + let salt_ptr = ptr_or_sentinel(&salt); #[repr(packed)] #[allow(dead_code)] struct Args { @@ -232,7 +233,7 @@ impl HostFn for HostFnImpl { address, output: output_ptr, output_len: &mut output_len as *mut _, - salt: salt.as_ptr(), + salt: salt_ptr, }; let ret_code = { unsafe { sys::instantiate(&args as *const Args as *const _) } }; -- GitLab From 0136463321dcf661740f9bfec2a0f0739487b60d Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 13 Sep 2024 13:31:17 +0200 Subject: [PATCH 210/217] pallet-migrations: fix index access for singluar migrations (#5695) Discovered a bug in the migrations pallet while debugging https://github.com/paritytech/try-runtime-cli/pull/90. It only occurs when a single MBM is configured - hence it did not happen when Ajuna Network tried it... Changes: - Check len of the tuple before accessing its nth_id - Make nth_id return `None` on unary tuples and n>0 --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: ggwpez --- prdoc/pr_5695.prdoc | 15 ++++++++++++++ substrate/frame/migrations/src/lib.rs | 7 ++++--- substrate/frame/migrations/src/tests.rs | 25 +++++++++++++++++++++++ substrate/frame/support/src/migrations.rs | 9 +++++--- 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 prdoc/pr_5695.prdoc diff --git a/prdoc/pr_5695.prdoc b/prdoc/pr_5695.prdoc new file mode 100644 index 00000000000..202eafa26eb --- /dev/null +++ b/prdoc/pr_5695.prdoc @@ -0,0 +1,15 @@ +title: 'pallet-migrations: fix index access for singluar migrations' +doc: +- audience: Runtime Dev + description: |- + Discovered a bug in the migrations pallet while debugging https://github.com/paritytech/try-runtime-cli/pull/90. + It only occurs when a single MBM is configured - hence it did not happen when Ajuna Network tried it... + + Changes: + - Check len of the tuple before accessing its nth_id + - Make nth_id return `None` on unary tuples and n>0 +crates: +- name: pallet-migrations + bump: patch +- name: frame-support + bump: patch diff --git a/substrate/frame/migrations/src/lib.rs b/substrate/frame/migrations/src/lib.rs index 68041a57eaa..1823e5a2f95 100644 --- a/substrate/frame/migrations/src/lib.rs +++ b/substrate/frame/migrations/src/lib.rs @@ -678,7 +678,7 @@ impl Pallet { return Some(ControlFlow::Break(cursor)) } - let Some(id) = T::Migrations::nth_id(cursor.index) else { + if cursor.index >= T::Migrations::len() { // No more migrations in the tuple - we are done. defensive_assert!(cursor.index == T::Migrations::len(), "Inconsistent MBMs tuple"); Self::deposit_event(Event::UpgradeCompleted); @@ -687,8 +687,9 @@ impl Pallet { return None; }; - let Ok(bounded_id): Result, _> = id.try_into() else { - defensive!("integrity_test ensures that all identifiers' MEL bounds fit into CursorMaxLen; qed."); + let id = T::Migrations::nth_id(cursor.index).map(TryInto::try_into); + let Some(Ok(bounded_id)): Option, _>> = id else { + defensive!("integrity_test ensures that all identifiers are present and bounde; qed."); Self::upgrade_failed(Some(cursor.index)); return None }; diff --git a/substrate/frame/migrations/src/tests.rs b/substrate/frame/migrations/src/tests.rs index 9c9043d37a6..73ca2a9a09c 100644 --- a/substrate/frame/migrations/src/tests.rs +++ b/substrate/frame/migrations/src/tests.rs @@ -27,6 +27,31 @@ use frame_support::{pallet_prelude::Weight, traits::OnRuntimeUpgrade}; #[docify::export] #[test] fn simple_works() { + use Event::*; + test_closure(|| { + // Add three migrations, each taking one block longer than the previous. + MockedMigrations::set(vec![(SucceedAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Check that the executed migrations are recorded in `Historical`. + assert_eq!(historic(), vec![mocked_id(SucceedAfter, 2),]); + + // Check that we got all events. + assert_events(vec![ + UpgradeStarted { migrations: 1 }, + MigrationAdvanced { index: 0, took: 1 }, + MigrationAdvanced { index: 0, took: 2 }, + MigrationCompleted { index: 0, took: 3 }, + UpgradeCompleted, + ]); + }); +} + +#[test] +fn simple_multiple_works() { use Event::*; test_closure(|| { // Add three migrations, each taking one block longer than the previous. diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs index 7f746146956..0eabf9d0ee1 100644 --- a/substrate/frame/support/src/migrations.rs +++ b/substrate/frame/support/src/migrations.rs @@ -673,7 +673,8 @@ pub trait SteppedMigrations { /// The `n`th [`SteppedMigration::id`]. /// - /// Is guaranteed to return `Some` if `n < Self::len()`. + /// Is guaranteed to return `Some` if `n < Self::len()`. Calling this with any index larger or + /// equal to `Self::len()` MUST return `None`. fn nth_id(n: u32) -> Option>; /// The [`SteppedMigration::max_steps`] of the `n`th migration. @@ -777,8 +778,10 @@ impl SteppedMigrations for T { 1 } - fn nth_id(_n: u32) -> Option> { - Some(T::id().encode()) + fn nth_id(n: u32) -> Option> { + n.is_zero() + .then_some(T::id().encode()) + .defensive_proof("nth_id should only be called with n==0") } fn nth_max_steps(n: u32) -> Option> { -- GitLab From fb7300ce15086de8333b22de823f5b02f2bb3280 Mon Sep 17 00:00:00 2001 From: Ron Date: Fri, 13 Sep 2024 20:09:47 +0800 Subject: [PATCH 211/217] Transfer Polkadot-native assets to Ethereum (#5546) # Description Adding support for send polkadot native assets(PNA) to Ethereum network through snowbridge. Asset with location in view of AH Including: - Relay token `(1,Here)` - Native asset `(0,[PalletInstance(instance),GenereIndex(index)])` managed by Assets Pallet - Native asset of Parachain `(1,[Parachain(paraId)])` managed by Foreign Assets Pallet The original PR in https://github.com/Snowfork/polkadot-sdk/pull/128 which has been internally reviewed by Snowbridge team. # Notes - This feature depends on the companion solidity change in https://github.com/Snowfork/snowbridge/pull/1155. Currently register PNA is only allowed from [sudo](https://github.com/Snowfork/polkadot-sdk/blob/46cb3528cd8cd1394af2335a6907d7ab8647717a/bridges/snowbridge/pallets/system/src/lib.rs#L621), so it's actually not enabled. Will require another runtime upgrade to make the call permissionless together with upgrading the Gateway contract. - To make things easy multi-hop transfer(i.e. sending PNA from Ethereum through AH to Destination chain) is not support ed in this PR. For this case user can switch to 2-phases transfer instead. --------- Co-authored-by: Clara van Staden Co-authored-by: Alistair Singh Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Co-authored-by: Francisco Aguirre Co-authored-by: Adrian Catangiu --- Cargo.lock | 1 + .../pallets/inbound-queue/src/lib.rs | 29 +- .../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 | 116 +++++- bridges/snowbridge/pallets/system/src/mock.rs | 9 +- .../snowbridge/pallets/system/src/tests.rs | 135 +++++- .../snowbridge/pallets/system/src/weights.rs | 11 + bridges/snowbridge/primitives/core/Cargo.toml | 2 + bridges/snowbridge/primitives/core/src/lib.rs | 34 +- .../primitives/core/src/location.rs | 205 +++++++++ .../primitives/core/src/outbound.rs | 57 +++ .../primitives/router/src/inbound/mod.rs | 169 +++++++- .../primitives/router/src/inbound/tests.rs | 31 +- .../primitives/router/src/outbound/mod.rs | 183 ++++++-- .../primitives/router/src/outbound/tests.rs | 393 +++++++++++------- .../assets/asset-hub-westend/src/genesis.rs | 8 + .../bridges/bridge-hub-westend/src/lib.rs | 1 + .../emulated/common/src/lib.rs | 9 + .../bridge-hub-westend/src/tests/mod.rs | 3 +- .../src/tests/snowbridge.rs | 384 ++++++++++++++--- .../src/bridge_to_ethereum_config.rs | 16 +- .../src/weights/snowbridge_pallet_system.rs | 10 + .../src/bridge_to_ethereum_config.rs | 13 +- .../src/weights/snowbridge_pallet_system.rs | 10 + .../runtimes/constants/src/rococo.rs | 3 +- .../runtimes/constants/src/westend.rs | 3 +- prdoc/pr_5546.prdoc | 36 ++ 29 files changed, 1626 insertions(+), 298 deletions(-) create mode 100644 bridges/snowbridge/primitives/core/src/location.rs create mode 100644 prdoc/pr_5546.prdoc diff --git a/Cargo.lock b/Cargo.lock index 5f0fa0f613f..85b0699fff7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19588,6 +19588,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 4a1486204eb..423b92b9fae 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; @@ -62,9 +61,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 +84,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 +275,12 @@ pub mod pallet { T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; } + // Decode payload into `VersionedMessage` + 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 +322,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 871df6d1e51..3e67d5ab738 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}; @@ -112,6 +112,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(1002)].into(); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); } #[cfg(feature = "runtime-benchmarks")] @@ -205,6 +208,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; @@ -218,6 +231,9 @@ impl inbound_queue::Config for Test { InboundQueuePalletInstance, AccountId, Balance, + MockTokenIdConvert, + UniversalLocation, + AssetHubFromEthereum, >; 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 d65a96e2702..0b34893333e 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 ef908ad6a3f..20798b7c349 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::parent(); + 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::Root, 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 39c73e3630e..1e8a788b7a5 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -35,8 +35,14 @@ //! //! 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; @@ -63,13 +69,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 +108,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()) } @@ -127,6 +136,7 @@ where #[frame_support::pallet] pub mod pallet { + use frame_support::dispatch::PostDispatchInfo; use snowbridge_core::StaticLookup; use sp_core::U256; @@ -164,6 +174,12 @@ pub mod pallet { type WeightInfo: WeightInfo; + /// This chain's Universal Location. + type UniversalLocation: Get; + + // The bridges configured Ethereum location + type EthereumLocation: Get; + #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; } @@ -211,6 +227,13 @@ pub mod pallet { PricingParametersChanged { params: PricingParametersOf, }, + /// Register Polkadot-native token as a wrapped ERC20 token on Ethereum + RegisterToken { + /// Location of Polkadot-native token + location: VersionedLocation, + /// ID of Polkadot-native token on Ethereum + foreign_token_id: H256, + }, } #[pallet::error] @@ -243,6 +266,16 @@ pub mod pallet { pub type PricingParameters = StorageValue<_, PricingParametersOf, ValueQuery, T::DefaultPricingParameters>; + /// 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 location relative to ethereum to foreign token ID + #[pallet::storage] + pub type NativeToForeignId = + StorageMap<_, Blake2_128Concat, xcm::v4::Location, TokenId, OptionQuery>; + #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { @@ -574,6 +607,34 @@ pub mod pallet { }); Ok(()) } + + /// 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 (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())] + pub fn register_token( + origin: OriginFor, + location: Box, + metadata: AssetMetadata, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + let location: Location = + (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; + + Self::do_register_token(&location, metadata, PaysFee::::No)?; + + Ok(PostDispatchInfo { + actual_weight: Some(T::WeightInfo::register_token()), + pays_fee: Pays::No, + }) + } } impl Pallet { @@ -663,6 +724,42 @@ pub mod pallet { let secondary_exists = Channels::::contains_key(SECONDARY_GOVERNANCE_CHANNEL); primary_exists && secondary_exists } + + pub(crate) fn do_register_token( + location: &Location, + metadata: AssetMetadata, + pays_fee: PaysFee, + ) -> Result<(), DispatchError> { + let ethereum_location = T::EthereumLocation::get(); + // reanchor to Ethereum context + let location = location + .clone() + .reanchored(ðereum_location, &T::UniversalLocation::get()) + .map_err(|_| Error::::LocationConversionFailed)?; + + let token_id = TokenIdOf::convert_location(&location) + .ok_or(Error::::LocationConversionFailed)?; + + if !ForeignToNativeId::::contains_key(token_id) { + NativeToForeignId::::insert(location.clone(), token_id); + ForeignToNativeId::::insert(token_id, location.clone()); + } + + 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 { + location: location.clone().into(), + foreign_token_id: token_id, + }); + + Ok(()) + } } impl StaticLookup for Pallet { @@ -684,4 +781,13 @@ pub mod pallet { PricingParameters::::get() } } + + impl MaybeEquivalence for Pallet { + fn convert(foreign_id: &TokenId) -> Option { + ForeignToNativeId::::get(foreign_id) + } + fn convert_back(location: &Location) -> Option { + NativeToForeignId::::get(location) + } + } } diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 98bd3da9ab2..47b089866a5 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -166,10 +166,12 @@ 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(); + 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; @@ -177,8 +179,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 +190,6 @@ parameter_types! { multiplier: FixedU128::from_rational(4, 3) }; pub const InboundDeliveryCost: u128 = 1_000_000_000; - } #[cfg(feature = "runtime-benchmarks")] @@ -208,6 +209,8 @@ impl crate::Config for Test { type DefaultPricingParameters = Parameters; type WeightInfo = (); type InboundDeliveryCost = InboundDeliveryCost; + type UniversalLocation = UniversalLocation; + type EthereumLocation = EthereumDestination; #[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 09f24195a30..d0286e04abd 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),); @@ -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); @@ -631,3 +631,116 @@ fn no_genesis_build_is_uninitialized() { assert!(!EthereumSystem::is_initialized(), "Ethereum initialized."); }); } + +#[test] +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)]); + let versioned_location: Box = Box::new(location.clone().into()); + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, Default::default()), + BadOrigin + ); + }); +} + +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 test_cases = vec![ + // DOT + RegisterTokenTestCase { + native: Location::parent(), + reanchored: Location::new(1, GlobalConsensus(Polkadot)), + 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(), + }, + // 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(), + }, + // KSM + RegisterTokenTestCase { + native: Location::new(2, [GlobalConsensus(Kusama)]), + reanchored: Location::new(1, [GlobalConsensus(Kusama)]), + 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(), + }, + ]; + for tc in test_cases.iter() { + new_test_ext(true).execute_with(|| { + 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_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(), + foreign_token_id: tc.foreign, + })); + }); + } +} + +#[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()); + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, Default::default()), + Error::::LocationConversionFailed + ); + }); +} diff --git a/bridges/snowbridge/pallets/system/src/weights.rs b/bridges/snowbridge/pallets/system/src/weights.rs index 6e532a0d8a8..3513097f8b5 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 f9bee1ff495..fa37c795b2d 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 ed1af4225d2..7ad129a5254 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, }; @@ -21,18 +23,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_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}; @@ -151,16 +151,24 @@ 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, +/// 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 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, } } } -/// 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)>; +/// Maximum length of a string field in ERC20 token metada +const METADATA_FIELD_MAX_LEN: u32 = 32; diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs new file mode 100644 index 00000000000..aad1c9ece05 --- /dev/null +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -0,0 +1,205 @@ +// 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, 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)>; + +pub type TokenId = H256; + +/// 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)>, +>; + +/// 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() { + (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))) => { + let mut tail = l.clone().split_first_interior().0; + tail.dec_parent(); + let interior = Suffix::describe_location(&tail)?; + Some((b"GlobalConsensus", 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((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"PalletInstance", *instance).encode()), + [PalletInstance(instance), GeneralIndex(index)] => + Some((b"PalletInstance", *instance, b"GeneralIndex", *index).encode()), + [PalletInstance(instance), GeneralKey { data, .. }] => + Some((b"PalletInstance", *instance, b"GeneralKey", *data).encode()), + + [PalletInstance(instance), AccountKey20 { key, .. }] => + Some((b"PalletInstance", *instance, b"AccountKey20", *key).encode()), + [PalletInstance(instance), AccountId32 { id, .. }] => + Some((b"PalletInstance", *instance, b"AccountId32", *id).encode()), + + // Reject all other locations + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use crate::TokenIdOf; + use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, + PalletInstance, Parachain, + }; + 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)]), + // Parachain cases + // Parachain relative to Ethereum + Location::new(1, [GlobalConsensus(Westend), 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 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), + 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] }, + ], + ), + ]; + + for token in token_locations { + assert!( + TokenIdOf::convert_location(&token).is_some(), + "Valid token = {token:?} yeilds no TokenId." + ); + } + + 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!( + TokenIdOf::convert_location(&token).is_none(), + "Invalid token = {token:?} yeilds a TokenId." + ); + } + } +} diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index 0ba0fdb6108..77770761822 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/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 54e47a7a8b6..5cff8413af6 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, + EthereumUniversalLocation, + GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, Balance: BalanceT, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: Get, + GlobalAssetHubLocation: Get, { _phantom: PhantomData<( CreateAssetCall, @@ -100,6 +118,9 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + EthereumUniversalLocation, + 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 @@ -115,51 +141,107 @@ 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]; -impl - ConvertMessage +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > ConvertMessage for MessageToXcm< CreateAssetCall, CreateAssetDeposit, InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: Get, + GlobalAssetHubLocation: Get, { 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(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 -where +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > + MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: 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(); @@ -202,6 +284,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(); @@ -209,6 +293,7 @@ where } fn convert_send_token( + message_id: H256, chain_id: u64, token: H160, destination: Destination, @@ -266,6 +351,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(), }, @@ -281,6 +368,9 @@ where }, } + // Forward message id to Asset Hub. + instructions.push(SetTopic(message_id.into())); + (instructions.into(), total_fees.into()) } @@ -291,6 +381,59 @@ where [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], ) } + + /// 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, + 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 beneficiary = match destination { + // Final destination is a 32-byte account on AssetHub + Destination::AccountId32 { id } => + Ok(Location::new(0, [AccountId32 { network: None, id }])), + _ => Err(ConvertMessageError::InvalidDestination), + }?; + + let total_fee_asset: Asset = (Location::parent(), asset_hub_fee).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(), &EthereumUniversalLocation::get()) + .map_err(|_| ConvertMessageError::CannotReanchor)?; + + let asset: Asset = (reanchored_asset_loc, amount).into(); + + let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); + + let 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()), + // 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 }, + 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(), asset_hub_fee.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 75670b05c10..e0e90e516be 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,32 @@ fn test_contract_location_with_incorrect_location_fails_convert() { None, ); } + +#[test] +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 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()); + } +} diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ddc36ce8cb6..d3b6c116dd7 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -12,9 +12,10 @@ use codec::{Decode, Encode}; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ outbound::{AgentExecuteCommand, Command, Message, SendMessage}, - ChannelId, ParaId, + 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,31 @@ pub struct EthereumBlobExporter< EthereumNetwork, OutboundQueue, AgentHashedDescription, ->(PhantomData<(UniversalLocation, EthereumNetwork, OutboundQueue, AgentHashedDescription)>); - -impl ExportXcm - for EthereumBlobExporter -where + 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 +104,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 +114,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 +169,9 @@ enum XcmConverterError { AssetResolutionFailed, InvalidFeeAsset, SetTopicExpected, + ReserveAssetDepositedExpected, + InvalidAsset, + UnexpectedInstruction, } macro_rules! match_expression { @@ -165,18 +183,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 +219,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 +293,13 @@ 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::AgentExecute { + agent_id: self.agent_id, + command: AgentExecuteCommand::TransferToken { token, recipient, amount }, + }, + *topic_id, + )) } fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { @@ -275,9 +312,95 @@ 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 } } + + /// 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::*; + + // 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 111243bb45a..6e4fd594634 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,11 +422,15 @@ fn xcm_converter_convert_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + 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]))); @@ -443,11 +459,15 @@ 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 { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + 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]))); @@ -478,11 +498,15 @@ 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 { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + 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]))); @@ -513,11 +537,15 @@ 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 { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + 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]))); @@ -547,7 +575,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 +593,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 +625,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 +656,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 +668,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 +701,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 +732,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 +758,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 +792,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 +831,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 +863,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 +895,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 +926,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 +960,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 +994,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 +1032,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 +1069,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 +1101,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 2876474e094..a9cfcda0dac 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, }; @@ -81,6 +82,13 @@ pub fn genesis() -> Storage { false, ED, ), + // PenpalB's teleportable asset representation + ( + PenpalBTeleportableAssetLocation::get(), + PenpalBSiblingSovereignAccount::get(), + false, + 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 feb59c411c8..e7a28ebf4a4 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 30e66ced1fb..c6b8889730e 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::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(); } /// Helper function to generate a crypto pair from seed 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 30cc4de3905..699641d3328 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,9 +18,10 @@ use crate::imports::*; mod asset_transfers; mod claim_assets; 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 b4db9b365f3..4e9dd5a77dd 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,24 @@ 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::RESERVABLE_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; +use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ - Command, ConvertMessage, Destination, MessageV1, VersionedMessage, + Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, }; +use sp_core::H256; 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 { @@ -55,20 +59,16 @@ 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).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), 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 +77,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 { .. }) => {},] ); }); } @@ -120,26 +118,22 @@ 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 { token: WETH.into(), destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, 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 assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] ); }); @@ -149,9 +143,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 +159,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(|| { @@ -194,26 +179,23 @@ 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, command: Command::SendToken { token: WETH.into(), destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, 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 assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] ); }); @@ -224,9 +206,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,9 +216,9 @@ 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)); + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); let destination = VersionedLocation::V4(Location::new( 2, @@ -259,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, ) @@ -279,10 +259,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 +282,316 @@ 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: [].into() }; + let expected_asset_id: Location = + Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; + + let expected_token_id = TokenIdOf::convert_location(&expected_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::root(), + 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 versioned_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(versioned_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, to, ..}) + if *amount == TOKEN_AMOUNT && *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{ .. }) => {},] + ); + + // 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: expected_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(); + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + 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) + .unwrap(); + + let token_id = TokenIdOf::convert_location(&asset_id_after_reanchored).unwrap(); + + // Register token + BridgeHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + 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 + 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 versioned_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(versioned_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." + ); + }); +} 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 6c0486c62fa..be7005b5379 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,9 +29,10 @@ 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; #[cfg(feature = "runtime-benchmarks")] use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; @@ -40,8 +41,7 @@ use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, }; - -pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; +use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -49,6 +49,7 @@ pub type SnowbridgeExporter = EthereumBlobExporter< EthereumNetwork, snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, + EthereumSystem, >; // Ethereum Bridge @@ -65,6 +66,8 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; + 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(); } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -85,6 +88,9 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { ConstU8, AccountId, Balance, + EthereumSystem, + EthereumUniversalLocation, + AssetHubFromEthereum, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; @@ -161,6 +167,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; @@ -181,6 +189,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumLocation = EthereumLocation; } #[cfg(feature = "runtime-benchmarks")] 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 c6c188e323a..3831111f097 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 47b6006ed6c..dbca4166a13 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,9 +30,10 @@ 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; #[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, InteriorLocation, Location, Parachain}; pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; @@ -50,6 +52,7 @@ pub type SnowbridgeExporter = EthereumBlobExporter< EthereumNetwork, snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, + EthereumSystem, >; // Ethereum Bridge @@ -66,8 +69,9 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; + 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 { type RuntimeEvent = RuntimeEvent; type Verifier = snowbridge_pallet_ethereum_client::Pallet; @@ -86,6 +90,9 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { ConstU8, AccountId, Balance, + EthereumSystem, + EthereumUniversalLocation, + AssetHubFromEthereum, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; @@ -181,6 +188,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumLocation = EthereumLocation; } #[cfg(feature = "runtime-benchmarks")] 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 c6c188e323a..3831111f097 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)) + } } diff --git a/cumulus/parachains/runtimes/constants/src/rococo.rs b/cumulus/parachains/runtimes/constants/src/rococo.rs index 56f4868371c..be4b5c9711c 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 fec66cec2eb..47ba8f7e97a 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()); } } diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc new file mode 100644 index 00000000000..95f02dbe13b --- /dev/null +++ b/prdoc/pr_5546.prdoc @@ -0,0 +1,36 @@ +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-outbound-queue + 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 + 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 + -- GitLab From ac27c5f5e8571ebfc0191f14ccb842dc2d17cba9 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 13 Sep 2024 16:27:52 +0200 Subject: [PATCH 212/217] =?UTF-8?q?[Bot]=C2=A0Fix=20backport=20bot=20(#568?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently changing the GH Token made it lose permission... error from here: https://github.com/paritytech/polkadot-sdk/actions/runs/10815755374/job/30005387859 From this list it looks like comment creation should be allowed by adding issues to the list: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token --- .github/workflows/command-backport.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml index 85e7b77801d..5b32f954d0c 100644 --- a/.github/workflows/command-backport.yml +++ b/.github/workflows/command-backport.yml @@ -9,6 +9,7 @@ on: permissions: contents: write # so it can comment pull-requests: write # so it can create pull requests + issues: write jobs: backport: -- GitLab From 51f336711a0391987db69d6281c9b57bfe49d925 Mon Sep 17 00:00:00 2001 From: Benjamin Gallois Date: Fri, 13 Sep 2024 18:10:17 +0200 Subject: [PATCH 213/217] Fix treasury benchmarks when no SpendOrigin (#3049) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Issue It was impossible to benchmark the pallet_treasury when `SpendOrigin = frame_support::traits::NeverEnsureOrigin;` was specified. ### Done - [x] Use `weight = 0` for all extrinsics that are un-callable with no `SpendOrigin`. - [x] Fix benchmarks for extrinsics requiring a Spend even if `SpendOrigin = frame_support::traits::NeverEnsureOrigin;` --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher Co-authored-by: Oliver Tale-Yazdi --- prdoc/pr_3049.prdoc | 11 ++ substrate/frame/treasury/src/benchmarking.rs | 178 +++++++++++++------ substrate/frame/treasury/src/tests.rs | 19 +- 3 files changed, 153 insertions(+), 55 deletions(-) create mode 100644 prdoc/pr_3049.prdoc diff --git a/prdoc/pr_3049.prdoc b/prdoc/pr_3049.prdoc new file mode 100644 index 00000000000..9cead8e2a4e --- /dev/null +++ b/prdoc/pr_3049.prdoc @@ -0,0 +1,11 @@ +title: "Fix treasury benchmarks when `SpendOrigin` being `None`" + +doc: + - audience: Runtime Dev + description: | + Fix treasury benchmarks when `SpendOrigin` not returning any succesful origin. + This is for example the case when `SpendOrigin` is set to `NeverOrigin`. + +crates: + - name: pallet-treasury + bump: patch diff --git a/substrate/frame/treasury/src/benchmarking.rs b/substrate/frame/treasury/src/benchmarking.rs index e63febb5798..0bac78503f4 100644 --- a/substrate/frame/treasury/src/benchmarking.rs +++ b/substrate/frame/treasury/src/benchmarking.rs @@ -26,7 +26,7 @@ use frame_benchmarking::{ v2::*, }; use frame_support::{ - ensure, + assert_err, assert_ok, ensure, traits::{ tokens::{ConversionFromAssetBalance, PaymentStatus}, EnsureOrigin, OnInitialize, @@ -73,12 +73,19 @@ fn setup_proposal, I: 'static>( // Create proposals that are approved for use in `on_initialize`. fn create_approved_proposals, I: 'static>(n: u32) -> Result<(), &'static str> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let spender = T::SpendOrigin::try_successful_origin(); + for i in 0..n { let (_, value, lookup) = setup_proposal::(i); - Treasury::::spend_local(origin.clone(), value, lookup)?; + + if let Ok(origin) = &spender { + Treasury::::spend_local(origin.clone(), value, lookup)?; + } + } + + if spender.is_ok() { + ensure!(Approvals::::get().len() == n as usize, "Not all approved"); } - ensure!(Approvals::::get().len() == n as usize, "Not all approved"); Ok(()) } @@ -106,8 +113,8 @@ fn create_spend_arguments, I: 'static>( mod benchmarks { use super::*; - // This benchmark is short-circuited if `SpendOrigin` cannot provide - // a successful origin, in which case `spend` is un-callable and can use weight=0. + /// This benchmark is short-circuited if `SpendOrigin` cannot provide + /// a successful origin, in which case `spend` is un-callable and can use weight=0. #[benchmark] fn spend_local() -> Result<(), BenchmarkError> { let (_, value, beneficiary_lookup) = setup_proposal::(SEED); @@ -155,6 +162,8 @@ mod benchmarks { Ok(()) } + /// This benchmark is short-circuited if `SpendOrigin` cannot provide + /// a successful origin, in which case `spend` is un-callable and can use weight=0. #[benchmark] fn spend() -> Result<(), BenchmarkError> { let origin = @@ -190,85 +199,135 @@ mod benchmarks { #[benchmark] fn payout() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, beneficiary, beneficiary_lookup) = create_spend_arguments::(SEED); T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; + + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind.clone()), + amount, + Box::new(beneficiary_lookup), + None, + )?; + + true + } else { + false + }; + T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount); let caller: T::AccountId = account("caller", 0, SEED); - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), 0u32); - - let id = match Spends::::get(0).unwrap().status { - PaymentState::Attempted { id, .. } => { - assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); - id - }, - _ => panic!("No payout attempt made"), - }; - assert_last_event::(Event::Paid { index: 0, payment_id: id }.into()); - assert!(Treasury::::payout(RawOrigin::Signed(caller).into(), 0u32).is_err()); + #[block] + { + let res = Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } + + if spend_exists { + let id = match Spends::::get(0).unwrap().status { + PaymentState::Attempted { id, .. } => { + assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); + id + }, + _ => panic!("No payout attempt made"), + }; + assert_last_event::(Event::Paid { index: 0, payment_id: id }.into()); + assert!(Treasury::::payout(RawOrigin::Signed(caller).into(), 0u32).is_err()); + } + Ok(()) } #[benchmark] fn check_status() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, beneficiary, beneficiary_lookup) = create_spend_arguments::(SEED); + T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; - T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount); + T::Paymaster::ensure_successful(&beneficiary, asset_kind.clone(), amount); let caller: T::AccountId = account("caller", 0, SEED); - Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32)?; - match Spends::::get(0).unwrap().status { - PaymentState::Attempted { id, .. } => { - T::Paymaster::ensure_concluded(id); - }, - _ => panic!("No payout attempt made"), + + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind), + amount, + Box::new(beneficiary_lookup), + None, + )?; + + Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32)?; + match Spends::::get(0).unwrap().status { + PaymentState::Attempted { id, .. } => { + T::Paymaster::ensure_concluded(id); + }, + _ => panic!("No payout attempt made"), + }; + + true + } else { + false }; - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), 0u32); + #[block] + { + let res = + Treasury::::check_status(RawOrigin::Signed(caller.clone()).into(), 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } if let Some(s) = Spends::::get(0) { assert!(!matches!(s.status, PaymentState::Attempted { .. })); } + Ok(()) } #[benchmark] fn void_spend() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, _, beneficiary_lookup) = create_spend_arguments::(SEED); T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; - assert!(Spends::::get(0).is_some()); + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind.clone()), + amount, + Box::new(beneficiary_lookup), + None, + )?; + assert!(Spends::::get(0).is_some()); + + true + } else { + false + }; + let origin = T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - #[extrinsic_call] - _(origin as T::RuntimeOrigin, 0u32); + #[block] + { + let res = Treasury::::void_spend(origin as T::RuntimeOrigin, 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } assert!(Spends::::get(0).is_none()); Ok(()) @@ -279,4 +338,15 @@ mod benchmarks { crate::tests::ExtBuilder::default().build(), crate::tests::Test ); + + mod no_spend_origin_tests { + use super::*; + + impl_benchmark_test_suite!( + Treasury, + crate::tests::ExtBuilder::default().spend_origin_succesful_origin_err().build(), + crate::tests::Test, + benchmarks_path = benchmarking + ); + } } diff --git a/substrate/frame/treasury/src/tests.rs b/substrate/frame/treasury/src/tests.rs index a895ea8151a..106bfb530a8 100644 --- a/substrate/frame/treasury/src/tests.rs +++ b/substrate/frame/treasury/src/tests.rs @@ -77,6 +77,9 @@ thread_local! { pub static PAID: RefCell> = RefCell::new(BTreeMap::new()); pub static STATUS: RefCell> = RefCell::new(BTreeMap::new()); pub static LAST_ID: RefCell = RefCell::new(0u64); + + #[cfg(feature = "runtime-benchmarks")] + pub static TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR: RefCell = RefCell::new(false); } /// paid balance for a given account and asset ids @@ -131,6 +134,7 @@ parameter_types! { pub TreasuryAccount: u128 = Treasury::account_id(); pub const SpendPayoutPeriod: u64 = 5; } + pub struct TestSpendOrigin; impl frame_support::traits::EnsureOrigin for TestSpendOrigin { type Success = u64; @@ -147,7 +151,11 @@ impl frame_support::traits::EnsureOrigin for TestSpendOrigin { } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { - Ok(RuntimeOrigin::root()) + if TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow()) { + Err(()) + } else { + Ok(frame_system::RawOrigin::Root.into()) + } } } @@ -187,11 +195,20 @@ pub struct ExtBuilder {} impl Default for ExtBuilder { fn default() -> Self { + #[cfg(feature = "runtime-benchmarks")] + TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow_mut() = false); + Self {} } } impl ExtBuilder { + #[cfg(feature = "runtime-benchmarks")] + pub fn spend_origin_succesful_origin_err(self) -> Self { + TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow_mut() = true); + self + } + pub fn build(self) -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { -- GitLab From 316b7a7ae6ef2b5aafe0f0dac7e3da039546f97a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 16 Sep 2024 10:50:29 +0200 Subject: [PATCH 214/217] Introduces `VerifyExistenceProof` trait (#5682) Introduces a trait for verifying existence proofs in the runtime. The trait is implemented for the 16 patricia merkle tree and the binary tree. --------- Co-authored-by: Shawn Tabrizi Co-authored-by: command-bot <> --- Cargo.lock | 3 + prdoc/pr_5682.prdoc | 15 ++ substrate/frame/support/Cargo.toml | 4 + substrate/frame/support/src/traits.rs | 3 + substrate/frame/support/src/traits/proving.rs | 135 ++++++++++++++++++ .../primitives/runtime/src/proving_trie.rs | 7 +- substrate/utils/binary-merkle-tree/Cargo.toml | 9 +- substrate/utils/binary-merkle-tree/src/lib.rs | 61 ++++---- 8 files changed, 204 insertions(+), 33 deletions(-) create mode 100644 prdoc/pr_5682.prdoc create mode 100644 substrate/frame/support/src/traits/proving.rs diff --git a/Cargo.lock b/Cargo.lock index 85b0699fff7..1478a543d14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1476,6 +1476,7 @@ dependencies = [ "array-bytes", "hash-db", "log", + "parity-scale-codec", "sp-core", "sp-runtime", "sp-tracing 16.0.0", @@ -6017,6 +6018,7 @@ dependencies = [ "aquamarine", "array-bytes", "assert_matches", + "binary-merkle-tree", "bitflags 1.3.2", "docify", "environmental", @@ -6050,6 +6052,7 @@ dependencies = [ "sp-std 14.0.0", "sp-timestamp", "sp-tracing 16.0.0", + "sp-trie", "sp-weights", "static_assertions", "tt-call", diff --git a/prdoc/pr_5682.prdoc b/prdoc/pr_5682.prdoc new file mode 100644 index 00000000000..2b05d73ef55 --- /dev/null +++ b/prdoc/pr_5682.prdoc @@ -0,0 +1,15 @@ +title: Introduces `VerifyExistenceProof` trait + +doc: + - audience: Runtime Dev + description: | + Introduces `VerifyExistenceProof` trait for verifying proofs in the runtime. + An implementation of the trait for binary and 16 patricia merkle tree is provided. + +crates: + - name: binary-merkle-tree + bump: major + - name: sp-runtime + bump: patch + - name: frame-support + bump: minor diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index 549059e261c..9e9741ee161 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -17,6 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { workspace = true } +binary-merkle-tree.workspace = true serde = { features = ["alloc", "derive"], workspace = true } codec = { features = [ "derive", @@ -44,6 +45,7 @@ sp-staking = { workspace = true } sp-weights = { workspace = true } sp-debug-derive = { workspace = true } sp-metadata-ir = { workspace = true } +sp-trie = { workspace = true } tt-call = { workspace = true } macro_magic = { workspace = true } frame-support-procedural = { workspace = true } @@ -73,6 +75,7 @@ sp-crypto-hashing = { workspace = true, default-features = true } [features] default = ["std"] std = [ + "binary-merkle-tree/std", "codec/std", "environmental/std", "frame-metadata/std", @@ -97,6 +100,7 @@ std = [ "sp-std/std", "sp-timestamp/std", "sp-tracing/std", + "sp-trie/std", "sp-weights/std", ] runtime-benchmarks = [ diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index f635ed32a12..6e59ff9d030 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -132,6 +132,9 @@ pub mod dynamic_params; pub mod tasks; pub use tasks::Task; +mod proving; +pub use proving::*; + #[cfg(feature = "try-runtime")] mod try_runtime; #[cfg(feature = "try-runtime")] diff --git a/substrate/frame/support/src/traits/proving.rs b/substrate/frame/support/src/traits/proving.rs new file mode 100644 index 00000000000..dc44f4cd68e --- /dev/null +++ b/substrate/frame/support/src/traits/proving.rs @@ -0,0 +1,135 @@ +// 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. + +//! Provides functionality for verifying proofs. + +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use sp_core::Hasher; + +/// Something that can verify the existence of some data in a given proof. +pub trait VerifyExistenceProof { + /// The proof type. + type Proof; + /// The hash type. + type Hash; + + /// Verify the given `proof`. + /// + /// Ensures that the `proof` was build for `root` and returns the proved data. + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, ()>; +} + +/// Implements [`VerifyExistenceProof`] using a binary merkle tree. +pub struct BinaryMerkleTreeProver(core::marker::PhantomData); + +impl VerifyExistenceProof for BinaryMerkleTreeProver +where + H::Out: Decode + Encode, +{ + type Proof = binary_merkle_tree::MerkleProof>; + type Hash = H::Out; + + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, ()> { + if proof.root != *root { + return Err(()); + } + + if binary_merkle_tree::verify_proof::( + &proof.root, + proof.proof, + proof.number_of_leaves, + proof.leaf_index, + &proof.leaf, + ) { + Ok(proof.leaf) + } else { + Err(()) + } + } +} + +/// Proof used by [`SixteenPatriciaMerkleTreeProver`] for [`VerifyExistenceProof`]. +#[derive(Encode, Decode)] +pub struct SixteenPatriciaMerkleTreeExistenceProof { + /// The key of the value to prove. + pub key: Vec, + /// The value for that the existence is proved. + pub value: Vec, + /// The encoded nodes to prove the existence of the data under `key`. + pub proof: Vec>, +} + +/// Implements [`VerifyExistenceProof`] using a 16-patricia merkle tree. +pub struct SixteenPatriciaMerkleTreeProver(core::marker::PhantomData); + +impl VerifyExistenceProof for SixteenPatriciaMerkleTreeProver { + type Proof = SixteenPatriciaMerkleTreeExistenceProof; + type Hash = H::Out; + + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, ()> { + sp_trie::verify_trie_proof::, _, _, _>( + &root, + &proof.proof, + [&(&proof.key, Some(&proof.value))], + ) + .map_err(drop) + .map(|_| proof.value) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_runtime::{proving_trie::BasicProvingTrie, traits::BlakeTwo256}; + + #[test] + fn verify_binary_merkle_tree_prover_works() { + let proof = binary_merkle_tree::merkle_proof::( + vec![b"hey".encode(), b"yes".encode()], + 1, + ); + let root = proof.root; + + assert_eq!( + BinaryMerkleTreeProver::::verify_proof(proof, &root).unwrap(), + b"yes".encode() + ); + } + + #[test] + fn verify_sixteen_patricia_merkle_tree_prover_works() { + let trie = BasicProvingTrie::::generate_for(vec![ + (0u32, &b"hey"[..]), + (1u32, &b"yes"[..]), + ]) + .unwrap(); + let proof = trie.create_single_value_proof(1u32).unwrap(); + let root = *trie.root(); + + let proof = SixteenPatriciaMerkleTreeExistenceProof { + key: 1u32.encode(), + value: b"yes"[..].encode(), + proof, + }; + + assert_eq!( + SixteenPatriciaMerkleTreeProver::::verify_proof(proof, &root).unwrap(), + b"yes"[..].encode() + ); + } +} diff --git a/substrate/primitives/runtime/src/proving_trie.rs b/substrate/primitives/runtime/src/proving_trie.rs index 688bf81e0d7..9a423f18284 100644 --- a/substrate/primitives/runtime/src/proving_trie.rs +++ b/substrate/primitives/runtime/src/proving_trie.rs @@ -139,7 +139,7 @@ impl BasicProvingTrie where Hashing: sp_core::Hasher, Key: Encode, - Value: Encode + Decode, + Value: Encode, { /// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs. pub fn generate_for(items: I) -> Result @@ -167,7 +167,10 @@ where /// 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 { + pub fn query(&self, key: Key) -> Option + where + Value: Decode, + { let trie = TrieDBBuilder::new(&self.db, &self.root).build(); key.using_encoded(|s| trie.get(s)) .ok()? diff --git a/substrate/utils/binary-merkle-tree/Cargo.toml b/substrate/utils/binary-merkle-tree/Cargo.toml index 087ec5fd6c6..9577d94ef0b 100644 --- a/substrate/utils/binary-merkle-tree/Cargo.toml +++ b/substrate/utils/binary-merkle-tree/Cargo.toml @@ -12,6 +12,7 @@ homepage.workspace = true workspace = true [dependencies] +codec = { workspace = true, features = ["derive"] } array-bytes = { optional = true, workspace = true, default-features = true } log = { optional = true, workspace = true } hash-db = { workspace = true } @@ -25,4 +26,10 @@ sp-runtime = { workspace = true, default-features = true } [features] debug = ["array-bytes", "log"] default = ["debug", "std"] -std = ["hash-db/std", "log/std", "sp-core/std", "sp-runtime/std"] +std = [ + "codec/std", + "hash-db/std", + "log/std", + "sp-core/std", + "sp-runtime/std", +] diff --git a/substrate/utils/binary-merkle-tree/src/lib.rs b/substrate/utils/binary-merkle-tree/src/lib.rs index f2d338cf028..f98ee060901 100644 --- a/substrate/utils/binary-merkle-tree/src/lib.rs +++ b/substrate/utils/binary-merkle-tree/src/lib.rs @@ -37,6 +37,7 @@ use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use codec::{Decode, Encode}; use hash_db::Hasher; /// Construct a root hash of a Binary Merkle Tree created from given leaves. @@ -87,7 +88,7 @@ where /// A generated merkle proof. /// /// The structure contains all necessary data to later on verify the proof and the leaf itself. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Encode, Decode)] pub struct MerkleProof { /// Root hash of generated merkle tree. pub root: H, @@ -100,9 +101,9 @@ pub struct MerkleProof { /// /// This is needed to detect a case where we have an odd number of leaves that "get promoted" /// to upper layers. - pub number_of_leaves: usize, + pub number_of_leaves: u32, /// Index of the leaf the proof is for (0-based). - pub leaf_index: usize, + pub leaf_index: u32, /// Leaf content. pub leaf: L, } @@ -121,13 +122,13 @@ trait Visitor { /// The method will also visit the `root` hash (level 0). /// /// The `index` is an index of `left` item. - fn visit(&mut self, index: usize, left: &Option, right: &Option); + fn visit(&mut self, index: u32, left: &Option, right: &Option); } /// No-op implementation of the visitor. impl Visitor for () { fn move_up(&mut self) {} - fn visit(&mut self, _index: usize, _left: &Option, _right: &Option) {} + fn visit(&mut self, _index: u32, _left: &Option, _right: &Option) {} } /// Construct a Merkle Proof for leaves given by indices. @@ -140,7 +141,7 @@ impl Visitor for () { /// # Panic /// /// The function will panic if given `leaf_index` is greater than the number of leaves. -pub fn merkle_proof(leaves: I, leaf_index: usize) -> MerkleProof +pub fn merkle_proof(leaves: I, leaf_index: u32) -> MerkleProof where H: Hasher, H::Out: Default + Copy + AsRef<[u8]>, @@ -151,7 +152,7 @@ where let mut leaf = None; let iter = leaves.into_iter().enumerate().map(|(idx, l)| { let hash = ::hash(l.as_ref()); - if idx == leaf_index { + if idx as u32 == leaf_index { leaf = Some(l); } hash @@ -160,11 +161,11 @@ where /// The struct collects a proof for single leaf. struct ProofCollection { proof: Vec, - position: usize, + position: u32, } impl ProofCollection { - fn new(position: usize) -> Self { + fn new(position: u32) -> Self { ProofCollection { proof: Default::default(), position } } } @@ -174,7 +175,7 @@ where self.position /= 2; } - fn visit(&mut self, index: usize, left: &Option, right: &Option) { + fn visit(&mut self, index: u32, left: &Option, right: &Option) { // we are at left branch - right goes to the proof. if self.position == index { if let Some(right) = right { @@ -190,7 +191,7 @@ where } } - let number_of_leaves = iter.len(); + let number_of_leaves = iter.len() as u32; let mut collect_proof = ProofCollection::new(leaf_index); let root = merkelize::(iter, &mut collect_proof); @@ -237,8 +238,8 @@ impl<'a, H, T: AsRef<[u8]>> From<&'a T> for Leaf<'a, H> { pub fn verify_proof<'a, H, P, L>( root: &'a H::Out, proof: P, - number_of_leaves: usize, - leaf_index: usize, + number_of_leaves: u32, + leaf_index: u32, leaf: L, ) -> bool where @@ -440,7 +441,7 @@ mod tests { assert!(verify_proof::( &proof0.root, proof0.proof.clone(), - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf, )); @@ -449,7 +450,7 @@ mod tests { assert!(verify_proof::( &proof1.root, proof1.proof, - data.len(), + data.len() as _, proof1.leaf_index, &proof1.leaf, )); @@ -458,7 +459,7 @@ mod tests { assert!(verify_proof::( &proof2.root, proof2.proof, - data.len(), + data.len() as _, proof2.leaf_index, &proof2.leaf )); @@ -479,7 +480,7 @@ mod tests { ) .into(), proof0.proof, - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf )); @@ -487,7 +488,7 @@ mod tests { assert!(!verify_proof::( &proof0.root.into(), vec![], - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf )); @@ -498,14 +499,14 @@ mod tests { // given let data = vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; - for l in 0..data.len() { + for l in 0..data.len() as u32 { // when let proof = merkle_proof::(data.clone(), l); // then assert!(verify_proof::( &proof.root, proof.proof, - data.len(), + data.len() as _, proof.leaf_index, &proof.leaf )); @@ -523,14 +524,14 @@ mod tests { } } - for l in 0..data.len() { + for l in 0..data.len() as u32 { // when let proof = merkle_proof::(data.clone(), l); // then assert!(verify_proof::( &proof.root, proof.proof, - data.len(), + data.len() as _, proof.leaf_index, &proof.leaf )); @@ -546,14 +547,14 @@ mod tests { data.push(format!("{}", i)); } - for l in (0..data.len()).step_by(13) { + for l in (0..data.len() as u32).step_by(13) { // when let proof = merkle_proof::(data.clone(), l); // then assert!(verify_proof::( &proof.root, proof.proof, - data.len(), + data.len() as _, proof.leaf_index, &proof.leaf )); @@ -747,24 +748,24 @@ mod tests { .map(|address| array_bytes::hex2bytes_unchecked(&address)) .collect::>(); - for l in 0..data.len() { + for l in 0..data.len() as u32 { // when let proof = merkle_proof::(data.clone(), l); assert_eq!(array_bytes::bytes2hex("", &proof.root), array_bytes::bytes2hex("", &root)); assert_eq!(proof.leaf_index, l); - assert_eq!(&proof.leaf, &data[l]); + assert_eq!(&proof.leaf, &data[l as usize]); // then assert!(verify_proof::( &proof.root, proof.proof, - data.len(), + data.len() as _, proof.leaf_index, &proof.leaf )); } - let proof = merkle_proof::(data.clone(), data.len() - 1); + let proof = merkle_proof::(data.clone(), data.len() as u32 - 1); assert_eq!( proof, @@ -788,8 +789,8 @@ mod tests { ) .into(), ], - number_of_leaves: data.len(), - leaf_index: data.len() - 1, + number_of_leaves: data.len() as _, + leaf_index: data.len() as u32 - 1, leaf: array_bytes::hex2array_unchecked::<_, 20>( "c26B34D375533fFc4c5276282Fa5D660F3d8cbcB" ) -- GitLab From 22bdc3e5d8bcf2f542fcf57cea686f2774cbb626 Mon Sep 17 00:00:00 2001 From: Muharem Date: Mon, 16 Sep 2024 13:05:09 +0200 Subject: [PATCH 215/217] Asset Hub: auto incremented asset id for trust backed assets (#5687) Setup auto incremented asset id to `50_000_000` for trust backed assets. In order to align with Polkadot/Kusama Asset Hub - https://github.com/polkadot-fellows/runtimes/pull/414 The next closes existing assets IDs in Rococo is `69_696_969`, in Westend is `88_228_866`. ### Migration **Stakeholders**: all clients providing asset creation functionality on Westend/Rococo Asset Hub This change does not break the API but introduces a new constraint. It implements an auto-incremented ID strategy for Trust-Backed Assets (50 pallet instance indexes on both networks), starting at ID 50,000,000. Each new asset must be created with an ID that is one greater than the last asset created. The next ID can be fetched from the `NextAssetId` storage item of the assets pallet. An empty `NextAssetId` storage item indicates no constraint on the next asset ID and can serve as a feature flag for this release. --- .../assets/asset-hub-rococo/src/lib.rs | 8 ++++++- .../assets/asset-hub-westend/src/lib.rs | 8 ++++++- prdoc/pr_5687.prdoc | 24 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5687.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 a4a2554b7af..efe4a4052a9 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -266,7 +266,7 @@ impl pallet_assets::Config for Runtime { type Freezer = AssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; - type CallbackHandle = (); + type CallbackHandle = pallet_assets::AutoIncAssetId; type AssetAccountDeposit = AssetAccountDeposit; type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; #[cfg(feature = "runtime-benchmarks")] @@ -1019,6 +1019,12 @@ pub type Migrations = ( cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, pallet_collator_selection::migration::v2::MigrationToV2, frame_support::migrations::RemovePallet, + // unreleased + pallet_assets::migration::next_asset_id::SetNextAssetId< + ConstU32<50_000_000>, + Runtime, + TrustBackedAssetsInstance, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); 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 6da2a0bc7b9..712b5c0ada9 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -263,7 +263,7 @@ impl pallet_assets::Config for Runtime { type Freezer = AssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; - type CallbackHandle = (); + type CallbackHandle = pallet_assets::AutoIncAssetId; type AssetAccountDeposit = AssetAccountDeposit; type RemoveItemsLimit = ConstU32<1000>; #[cfg(feature = "runtime-benchmarks")] @@ -1021,6 +1021,12 @@ pub type Migrations = ( // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, + // unreleased + pallet_assets::migration::next_asset_id::SetNextAssetId< + ConstU32<50_000_000>, + Runtime, + TrustBackedAssetsInstance, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); diff --git a/prdoc/pr_5687.prdoc b/prdoc/pr_5687.prdoc new file mode 100644 index 00000000000..f84f8e72226 --- /dev/null +++ b/prdoc/pr_5687.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: "Westend/Rococo Asset Hub: auto incremented asset id for trust backed assets" + +doc: + - audience: Runtime User + description: | + Setup auto incremented asset id to `50_000_000` for trust backed assets. + + ### Migration + This change does not break the API but introduces a new constraint. It implements + an auto-incremented ID strategy for Trust-Backed Assets (50 pallet instance indexes on both + networks), starting at ID 50,000,000. Each new asset must be created with an ID that is one + greater than the last asset created. The next ID can be fetched from the `NextAssetId` + storage item of the assets pallet. An empty `NextAssetId` storage item indicates no + constraint on the next asset ID and can serve as a feature flag for this release. + + +crates: + - name: asset-hub-rococo-runtime + bump: major + - name: asset-hub-westend-runtime + bump: major -- GitLab From 655382fa87cd82f12920e316af63084ae098a4e3 Mon Sep 17 00:00:00 2001 From: ordian Date: Mon, 16 Sep 2024 14:09:16 +0200 Subject: [PATCH 216/217] fix rococo-dev bench (#5688) the range should always contain at least 2 values for the benchmark to work closes #5680 --- .../parachains/src/paras_inherent/benchmarking.rs | 4 ++-- prdoc/pr_5688.prdoc | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5688.prdoc diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index fa466de1198..266860061be 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -17,7 +17,7 @@ use super::*; use crate::{inclusion, ParaId}; use alloc::collections::btree_map::BTreeMap; -use core::cmp::min; +use core::cmp::{max, min}; use frame_benchmarking::{benchmarks, impl_benchmark_test_suite}; use frame_system::RawOrigin; @@ -107,7 +107,7 @@ benchmarks! { // of a single backed candidate. enter_backed_candidates_variable { let v in (BenchBuilder::::fallback_min_backing_votes()) - ..(BenchBuilder::::fallback_max_validators_per_core()); + .. max(BenchBuilder::::fallback_min_backing_votes() + 1, BenchBuilder::::fallback_max_validators_per_core()); let cores_with_backed: BTreeMap<_, _> = vec![(0, v)] // The backed candidate will have `v` validity votes. diff --git a/prdoc/pr_5688.prdoc b/prdoc/pr_5688.prdoc new file mode 100644 index 00000000000..88e712fcba6 --- /dev/null +++ b/prdoc/pr_5688.prdoc @@ -0,0 +1,10 @@ +title: "Fix `paras_inherent` benchmark" + +doc: + - audience: Runtime Dev + description: | + Fixes the benchmark for relay chains like rococo-dev with `max_validators_per_core` value lower than 2. + +crates: +- name: polkadot-runtime-parachains + bump: patch -- GitLab From 1e25ada05b51dd81219e1dbc1d32da823b3b66b2 Mon Sep 17 00:00:00 2001 From: Evgeny Snitko Date: Mon, 16 Sep 2024 18:03:53 +0400 Subject: [PATCH 217/217] Preflight workflow (#5692) Preflight reusable workflow - aggregated workflow to perform all preliminary jobs It currently includes: - check changed files - `set-image` (and runner) job - useful variables --- .github/workflows/build-misc.yml | 36 ++--- .github/workflows/build-publish-images.yml | 141 +++++++----------- .../workflows/check-cargo-check-runtimes.yml | 67 ++++----- .../workflows/check-frame-omni-bencher.yml | 45 ++---- .github/workflows/check-runtime-migration.yml | 33 +--- .github/workflows/checks-quick.yml | 37 ++--- .github/workflows/checks.yml | 56 ++----- .github/workflows/command-prdoc.yml | 16 +- .github/workflows/docs.yml | 29 ++-- .../reusable-check-changed-files.yml | 59 -------- .github/workflows/reusable-preflight.yml | 131 ++++++++++++++++ .github/workflows/subsystem-benchmarks.yml | 29 +--- .../workflows/tests-linux-stable-coverage.yml | 36 +---- .github/workflows/tests-linux-stable.yml | 60 ++------ .github/workflows/tests-misc.yml | 96 +++++------- .github/workflows/tests.yml | 54 ++----- 16 files changed, 359 insertions(+), 566 deletions(-) delete mode 100644 .github/workflows/reusable-check-changed-files.yml create mode 100644 .github/workflows/reusable-preflight.yml diff --git a/.github/workflows/build-misc.yml b/.github/workflows/build-misc.yml index a01384dc002..2a8e81b9787 100644 --- a/.github/workflows/build-misc.yml +++ b/.github/workflows/build-misc.yml @@ -16,34 +16,16 @@ 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 + + preflight: + uses: ./.github/workflows/reusable-preflight.yml build-runtimes-polkavm: timeout-minutes: 20 - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -64,10 +46,10 @@ jobs: build-subkey: timeout-minutes: 20 - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/build-publish-images.yml b/.github/workflows/build-publish-images.yml index 735b727e58b..3a9f6076186 100644 --- a/.github/workflows/build-publish-images.yml +++ b/.github/workflows/build-publish-images.yml @@ -19,41 +19,10 @@ 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 + preflight: + ## TODO: remove when ready + if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') + uses: ./.github/workflows/reusable-preflight.yml ### Build ######################## @@ -61,11 +30,11 @@ jobs: # # build-linux-stable: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUST_TOOLCHAIN: stable # Enable debug assertions since we are running optimized builds for testing @@ -85,7 +54,7 @@ jobs: - name: pack artifacts run: | mkdir -p ./artifacts - VERSION="${{ needs.set-image.outputs.REF_NAME }}" # will be tag or branch name + VERSION="${{ needs.preflight.outputs.SOURCE_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/. @@ -94,7 +63,7 @@ jobs: sha256sum polkadot | tee polkadot.sha256 shasum -c polkadot.sha256 cd ../ - EXTRATAG="${{ needs.set-image.outputs.REF_NAME }}-${COMMIT_SHA}" + EXTRATAG="${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" echo "Polkadot version = ${VERSION} (EXTRATAG = ${EXTRATAG})" echo -n ${VERSION} > ./artifacts/VERSION echo -n ${EXTRATAG} > ./artifacts/EXTRATAG @@ -109,7 +78,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} path: artifacts.tar retention-days: 1 @@ -117,11 +86,11 @@ jobs: # # build-linux-stable-cumulus: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" steps: @@ -135,7 +104,7 @@ jobs: 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 + echo ${{ needs.preflight.outputs.SOURCE_REF_NAME }} | tee ./artifacts/VERSION - name: tar run: tar -cvf artifacts.tar artifacts @@ -143,7 +112,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} path: artifacts.tar retention-days: 1 @@ -151,11 +120,11 @@ jobs: # # build-test-parachain: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" steps: @@ -179,7 +148,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} path: artifacts.tar retention-days: 1 @@ -187,11 +156,11 @@ jobs: # # build-test-collators: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -204,8 +173,8 @@ jobs: 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 -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}" > ./artifacts/VERSION + echo -n "${{ needs.preflight.outputs.SOURCE_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 @@ -216,7 +185,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} path: artifacts.tar retention-days: 1 @@ -224,11 +193,11 @@ jobs: # # build-malus: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -241,8 +210,8 @@ jobs: 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 -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}" > ./artifacts/VERSION + echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG echo "polkadot-test-malus = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" cp -r ./docker/* ./artifacts @@ -252,7 +221,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} path: artifacts.tar retention-days: 1 @@ -260,11 +229,11 @@ jobs: # # build-linux-substrate: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -296,7 +265,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} path: artifacts.tar retention-days: 1 @@ -304,11 +273,11 @@ jobs: # # prepare-bridges-zombienet-artifacts: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -326,7 +295,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} path: artifacts.tar retention-days: 1 @@ -336,7 +305,7 @@ jobs: # # build-push-image-test-parachain: - needs: [set-image, build-test-parachain] + needs: [preflight, build-test-parachain] runs-on: arc-runners-polkadot-sdk timeout-minutes: 60 steps: @@ -345,7 +314,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-test-parachain-${{ needs.set-image.outputs.REF_NAME }} + name: build-test-parachain-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: tar -xvf artifacts.tar @@ -360,7 +329,7 @@ jobs: # # build-push-image-polkadot-debug: - needs: [set-image, build-linux-stable] + needs: [preflight, build-linux-stable] runs-on: arc-runners-polkadot-sdk timeout-minutes: 60 steps: @@ -369,7 +338,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-stable-${{ needs.set-image.outputs.REF_NAME }} + name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: tar -xvf artifacts.tar @@ -385,7 +354,7 @@ jobs: # # build-push-image-colander: - needs: [set-image, build-test-collators] + needs: [preflight, build-test-collators] runs-on: arc-runners-polkadot-sdk timeout-minutes: 60 steps: @@ -394,7 +363,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-test-collators-${{ needs.set-image.outputs.REF_NAME }} + name: build-test-collators-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: tar -xvf artifacts.tar @@ -410,7 +379,7 @@ jobs: # # build-push-image-malus: - needs: [set-image, build-malus] + needs: [preflight, build-malus] runs-on: arc-runners-polkadot-sdk timeout-minutes: 60 steps: @@ -419,7 +388,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-malus-${{ needs.set-image.outputs.REF_NAME }} + name: build-malus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: tar -xvf artifacts.tar @@ -435,7 +404,7 @@ jobs: # # build-push-image-substrate-pr: - needs: [set-image, build-linux-substrate] + needs: [preflight, build-linux-substrate] runs-on: arc-runners-polkadot-sdk timeout-minutes: 60 steps: @@ -444,7 +413,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-substrate-${{ needs.set-image.outputs.REF_NAME }} + name: build-linux-substrate-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: tar -xvf artifacts.tar @@ -462,7 +431,7 @@ jobs: # 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] + needs: [preflight, build-linux-stable, build-linux-stable-cumulus, prepare-bridges-zombienet-artifacts] runs-on: arc-runners-polkadot-sdk timeout-minutes: 60 steps: @@ -471,7 +440,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-stable-${{ needs.set-image.outputs.REF_NAME }} + name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: | tar -xvf artifacts.tar @@ -479,7 +448,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-stable-cumulus-${{ needs.set-image.outputs.REF_NAME }} + name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: | tar -xvf artifacts.tar @@ -487,7 +456,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: prepare-bridges-zombienet-artifacts-${{ needs.set-image.outputs.REF_NAME }} + name: prepare-bridges-zombienet-artifacts-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: | tar -xvf artifacts.tar @@ -504,7 +473,7 @@ jobs: # # build-push-image-polkadot-parachain-debug: - needs: [set-image, build-linux-stable-cumulus] + needs: [preflight, build-linux-stable-cumulus] runs-on: arc-runners-polkadot-sdk timeout-minutes: 60 steps: @@ -513,7 +482,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-stable-cumulus-${{ needs.set-image.outputs.REF_NAME }} + name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: tar -xvf artifacts.tar diff --git a/.github/workflows/check-cargo-check-runtimes.yml b/.github/workflows/check-cargo-check-runtimes.yml index bfca4968a3b..16263788b8b 100644 --- a/.github/workflows/check-cargo-check-runtimes.yml +++ b/.github/workflows/check-cargo-check-runtimes.yml @@ -11,33 +11,16 @@ on: # Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers 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 + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + check-runtime-assets: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] timeout-minutes: 20 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -47,11 +30,11 @@ jobs: root: cumulus/parachains/runtimes/assets check-runtime-collectives: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [check-runtime-assets, set-image] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [check-runtime-assets, preflight] timeout-minutes: 20 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -61,10 +44,10 @@ jobs: root: cumulus/parachains/runtimes/collectives check-runtime-coretime: - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-assets, set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-assets, preflight] timeout-minutes: 20 steps: - name: Checkout @@ -75,10 +58,10 @@ jobs: root: cumulus/parachains/runtimes/coretime check-runtime-bridge-hubs: - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [preflight] timeout-minutes: 20 steps: - name: Checkout @@ -89,10 +72,10 @@ jobs: root: cumulus/parachains/runtimes/bridge-hubs check-runtime-contracts: - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-collectives, set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-collectives, preflight] timeout-minutes: 20 steps: - name: Checkout @@ -103,10 +86,10 @@ jobs: root: cumulus/parachains/runtimes/contracts check-runtime-starters: - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-assets, set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-assets, preflight] timeout-minutes: 20 steps: - name: Checkout @@ -117,10 +100,10 @@ jobs: root: cumulus/parachains/runtimes/starters check-runtime-testing: - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-starters, set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-starters, preflight] timeout-minutes: 20 steps: - name: Checkout diff --git a/.github/workflows/check-frame-omni-bencher.yml b/.github/workflows/check-frame-omni-bencher.yml index e035a30c7c2..9b01311aa69 100644 --- a/.github/workflows/check-frame-omni-bencher.yml +++ b/.github/workflows/check-frame-omni-bencher.yml @@ -16,39 +16,16 @@ env: ARTIFACTS_NAME: frame-omni-bencher-artifacts jobs: - changes: + + preflight: # 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 - 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 + uses: ./.github/workflows/reusable-preflight.yml quick-benchmarks-omni: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + if: ${{ needs.preflight.outputs.changes_rust }} env: RUSTFLAGS: "-C debug-assertions" RUST_BACKTRACE: "full" @@ -56,7 +33,7 @@ jobs: WASM_BUILD_RUSTFLAGS: "-C debug-assertions" timeout-minutes: 30 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -66,9 +43,9 @@ jobs: 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: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image, changes] # , build-frame-omni-bencher ] - if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] # , build-frame-omni-bencher ] + if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 30 strategy: fail-fast: false # keep running other workflows even if one fails, to see the logs of all possible failures @@ -89,7 +66,7 @@ jobs: glutton-westend-runtime, ] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: PACKAGE_NAME: ${{ matrix.runtime }} steps: diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index 0a1dbc4790c..8185cf171ae 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -17,38 +17,19 @@ concurrency: 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. - 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 + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + # More info can be found here: https://github.com/paritytech/polkadot/pull/5865 check-runtime-migration: - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.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 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} strategy: fail-fast: false matrix: diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml index c936e7c8938..9e36d2bcb2e 100644 --- a/.github/workflows/checks-quick.yml +++ b/.github/workflows/checks-quick.yml @@ -15,25 +15,16 @@ concurrency: permissions: {} 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 - 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 + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + fmt: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Cargo fmt @@ -50,9 +41,9 @@ jobs: check-rust-feature-propagation: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: fetch deps @@ -66,9 +57,9 @@ jobs: test-rust-features: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: run rust features @@ -76,9 +67,9 @@ jobs: check-toml-format: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: check toml format @@ -130,9 +121,9 @@ jobs: check-umbrella: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.0 (22. Sep 2023) - name: install python deps diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index cba7df51742..f765d79254c 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -15,41 +15,17 @@ concurrency: 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 + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + cargo-clippy: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 40 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-D warnings" SKIP_WASM_BUILD: 1 @@ -60,12 +36,12 @@ jobs: 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 }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 40 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script @@ -79,12 +55,12 @@ jobs: 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 }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 30 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script diff --git a/.github/workflows/command-prdoc.yml b/.github/workflows/command-prdoc.yml index 3a08b9a5fb2..aa9de9474a7 100644 --- a/.github/workflows/command-prdoc.yml +++ b/.github/workflows/command-prdoc.yml @@ -43,21 +43,15 @@ 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 + preflight: + uses: ./.github/workflows/reusable-preflight.yml + cmd-prdoc: - needs: [set-image] + needs: [preflight] runs-on: ubuntu-latest timeout-minutes: 20 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} permissions: contents: write pull-requests: write diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 523c2b19ba8..514bac3973b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,25 +13,14 @@ concurrency: 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 + preflight: + uses: ./.github/workflows/reusable-preflight.yml + test-rustdoc: runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@v4 - run: forklift cargo doc --workspace --all-features --no-deps @@ -39,9 +28,9 @@ jobs: SKIP_WASM_BUILD: 1 test-doc: runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@v4 - run: forklift cargo test --doc --workspace @@ -50,9 +39,9 @@ jobs: build-rustdoc: runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image, test-rustdoc] + needs: [preflight, test-rustdoc] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - uses: actions/checkout@v4 - run: forklift cargo doc --all-features --workspace --no-deps diff --git a/.github/workflows/reusable-check-changed-files.yml b/.github/workflows/reusable-check-changed-files.yml deleted file mode 100644 index 47f0620439c..00000000000 --- 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/reusable-preflight.yml b/.github/workflows/reusable-preflight.yml new file mode 100644 index 00000000000..71823a97ff2 --- /dev/null +++ b/.github/workflows/reusable-preflight.yml @@ -0,0 +1,131 @@ +# Reusable workflow to set various useful variables +# and 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: +# preflight: +# uses: ./.github/workflows/reusable-preflight.yml +# some-job: +# needs: changes +# if: ${{ needs.preflight.outputs.changes_rust }} +# ....... + +name: Preflight + +on: + workflow_call: + # Map the workflow outputs to job outputs + outputs: + changes_rust: + value: ${{ jobs.preflight.outputs.changes_rust }} + changes_currentWorkflow: + value: ${{ jobs.preflight.outputs.changes_currentWorkflow }} + + IMAGE: + value: ${{ jobs.preflight.outputs.IMAGE }} + description: "CI image" + RUNNER: + value: ${{ jobs.preflight.outputs.RUNNER }} + description: | + Runner name. + 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. + OLDLINUXRUNNER: + value: ${{ jobs.preflight.outputs.OLDLINUXRUNNER }} + + SOURCE_REF_NAME: + value: ${{ jobs.preflight.outputs.SOURCE_REF_NAME }} + description: "Name of the current branch for `push` or source branch for `pull_request`" + COMMIT_SHA: + value: ${{ jobs.preflight.outputs.COMMIT_SHA }} + description: "Sha of the current commit for `push` or head of the source branch for `pull_request`" + +jobs: + preflight: + runs-on: ubuntu-latest + outputs: + changes_rust: ${{ steps.set_changes.outputs.rust_any_changed || steps.set_changes.outputs.currentWorkflow_any_changed }} + changes_currentWorkflow: ${{ steps.set_changes.outputs.currentWorkflow_any_changed }} + + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} + OLDLINUXRUNNER: ${{ steps.set_runner.outputs.OLDLINUXRUNNER }} + + SOURCE_REF_NAME: ${{ steps.set_vars.outputs.SOURCE_REF_NAME }} + COMMIT_SHA: ${{ steps.set_vars.outputs.COMMIT_SHA }} + + + steps: + + - uses: actions/checkout@v4 + + # + # Set changes + # + - id: current_file + shell: bash + run: | + echo "currentWorkflowFile=$(echo ${{ github.workflow_ref }} | sed -nE "s/.*(\.github\/workflows\/[a-zA-Z0-9_-]*\.y[a]?ml)@refs.*/\1/p")" >> $GITHUB_OUTPUT + echo "currentActionDir=$(echo ${{ github.action_path }} | sed -nE "s/.*(\.github\/actions\/[a-zA-Z0-9_-]*)/\1/p")" >> $GITHUB_OUTPUT + + - name: Set changes + id: set_changes + uses: tj-actions/changed-files@v45 + with: + files_yaml: | + rust: + - '**/*' + - '!.github/**/*' + - '!prdoc/**/*' + - '!docs/**/*' + currentWorkflow: + - '${{ steps.current_file.outputs.currentWorkflowFile }}' + - '.github/workflows/reusable-preflight.yml' + + # + # Set image + # + - name: Set image + id: set_image + shell: bash + run: cat .github/env >> $GITHUB_OUTPUT + + # + # Set runner + # + # 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 + shell: bash + 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 + + # + # Set vars + # + - id: set_vars + shell: bash + run: | + export BRANCH_NAME=${{ github.head_ref || github.ref_name }} + echo "SOURCE_REF_NAME=${BRANCH_NAME//\//-}" >> $GITHUB_OUTPUT + echo "COMMIT_SHA=${{ github.event.pull_request.head.sha || github.sha }}" >> $GITHUB_OUTPUT + + + - name: log + shell: bash + run: | + echo "workflow file: ${{ steps.current_file.outputs.currentWorkflowFile }}" + echo "Modified: ${{ steps.set_changes.outputs.modified_keys }}" + echo "github.ref: ${{ github.ref }}" + echo "github.ref_name: ${{ github.ref_name }}" + echo "github.sha: ${{ github.sha }}" diff --git a/.github/workflows/subsystem-benchmarks.yml b/.github/workflows/subsystem-benchmarks.yml index 6f9db9da9e6..210714d847f 100644 --- a/.github/workflows/subsystem-benchmarks.yml +++ b/.github/workflows/subsystem-benchmarks.yml @@ -16,34 +16,15 @@ 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 + preflight: + uses: ./.github/workflows/reusable-preflight.yml build: timeout-minutes: 80 - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} strategy: fail-fast: false matrix: diff --git a/.github/workflows/tests-linux-stable-coverage.yml b/.github/workflows/tests-linux-stable-coverage.yml index 4c0a2629e41..90d7bc34a92 100644 --- a/.github/workflows/tests-linux-stable-coverage.yml +++ b/.github/workflows/tests-linux-stable-coverage.yml @@ -13,39 +13,19 @@ concurrency: 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. + preflight: + uses: ./.github/workflows/reusable-preflight.yml 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 }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 120 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUST_TOOLCHAIN: stable # Enable debug assertions since we are running optimized builds for testing @@ -128,16 +108,16 @@ jobs: 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' + if: github.event_name == 'pull_request' steps: - uses: actions/checkout@v4 - uses: actions-ecosystem/action-remove-labels@v1 with: - labels: GHA-coverage \ No newline at end of file + labels: GHA-coverage \ No newline at end of file diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml index 7ed67703395..6cf71422511 100644 --- a/.github/workflows/tests-linux-stable.yml +++ b/.github/workflows/tests-linux-stable.yml @@ -13,47 +13,17 @@ concurrency: 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 - 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 }} - 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 + preflight: + uses: ./.github/workflows/reusable-preflight.yml test-linux-stable-int: - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: 1 @@ -69,12 +39,12 @@ jobs: # https://github.com/paritytech/ci_cd/issues/864 test-linux-stable-runtime-benchmarks: - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUST_TOOLCHAIN: stable # Enable debug assertions since we are running optimized builds for testing @@ -87,8 +57,8 @@ jobs: run: forklift cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet test-linux-stable: - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} runs-on: ${{ matrix.runners }} timeout-minutes: 60 strategy: @@ -97,11 +67,11 @@ jobs: partition: [1/3, 2/3, 3/3] runners: [ - "${{ needs.set-image.outputs.RUNNER }}", - "${{ needs.set-image.outputs.OLDLINUXRUNNER }}", + "${{ needs.preflight.outputs.RUNNER }}", + "${{ needs.preflight.outputs.OLDLINUXRUNNER }}", ] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} # needed for tests that use unshare syscall options: --security-opt seccomp=unconfined env: diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index 90685743a41..0f2b617b847 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -14,45 +14,18 @@ concurrency: # 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 + + preflight: + uses: ./.github/workflows/reusable-preflight.yml # 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 }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. @@ -72,10 +45,10 @@ jobs: 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 }} + needs: [preflight, test-full-crypto-feature] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. @@ -93,10 +66,10 @@ jobs: test-frame-ui: timeout-minutes: 60 - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. @@ -121,10 +94,10 @@ jobs: test-deterministic-wasm: timeout-minutes: 20 - needs: [set-image, test-frame-examples-compile-to-wasm] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight, test-frame-examples-compile-to-wasm] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: WASM_BUILD_NO_COLOR: 1 steps: @@ -143,15 +116,15 @@ jobs: sha256sum -c checksum.sha256 cargo-check-benches: - needs: [set-image] + needs: [preflight] if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} timeout-minutes: 60 strategy: matrix: branch: [master, current] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -183,7 +156,7 @@ jobs: if: always() && !cancelled() # runs-on: arc-runners-polkadot-sdk runs-on: ubuntu-latest - needs: [set-image, cargo-check-benches] + needs: [preflight, cargo-check-benches] steps: - name: Checkout uses: actions/checkout@v4 @@ -219,11 +192,11 @@ jobs: fi test-node-metrics: - needs: [set-image] + needs: [preflight] timeout-minutes: 30 - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -252,10 +225,10 @@ jobs: # 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 }} + needs: [preflight, test-node-metrics] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -267,10 +240,10 @@ jobs: check-metadata-hash: timeout-minutes: 20 - needs: [set-image, check-tracing] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight, check-tracing] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -281,10 +254,10 @@ jobs: cargo-hfuzz: timeout-minutes: 20 - needs: [set-image, check-metadata-hash] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight, check-metadata-hash] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: # max 10s per iteration, 60s per file HFUZZ_RUN_ARGS: | @@ -321,10 +294,10 @@ jobs: cargo-check-each-crate: timeout-minutes: 140 - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-D warnings" CI_JOB_NAME: cargo-check-each-crate @@ -348,6 +321,7 @@ jobs: cargo-check-all-crate-macos: timeout-minutes: 30 + needs: [ preflight ] runs-on: parity-macos env: SKIP_WASM_BUILD: 1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ed2ef07736b..1132c2ca4dd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,44 +12,18 @@ concurrency: cancel-in-progress: true jobs: - # 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 + preflight: + uses: ./.github/workflows/reusable-preflight.yml # This job runs all benchmarks defined in the `/bin/node/runtime` once to check that there are no errors. quick-benchmarks: - needs: [ set-image ] - # if: ${{ needs.changes.outputs.rust }} - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [ preflight ] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: "full" @@ -63,12 +37,12 @@ jobs: # cf https://github.com/paritytech/polkadot-sdk/issues/1652 test-syscalls: - needs: [ set-image ] - # if: ${{ needs.changes.outputs.rust }} - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [ preflight ] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} continue-on-error: true # this rarely triggers in practice env: SKIP_WASM_BUILD: 1 @@ -89,12 +63,12 @@ jobs: cargo-check-all-benches: - needs: [ set-image ] - # if: ${{ needs.changes.outputs.rust }} - runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [ preflight ] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: SKIP_WASM_BUILD: 1 steps: -- GitLab
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